我有一个类,通过它我可以异步提供一些服务(同样的调用也可以同步进行)。当请求时,此类的对象(比如运算符)在另一个线程中启动操作。其他对象可以注册到操作符对象的通知,以便在操作结束时对此对象调用OperationEnded()方法。其他对象也可以通过调用操作符对象上的Wait()来等待此操作的完成。
操作结束时的代码大致如下:
_opEndedMutex.lock();
_thereIsOngoingOp = false;
_opEndedCondition.notify_all();
_opEndedMutex.unlock();
//no more call after notification
m_spNotificationManager->OperationEnded();
和wait()函数如下:
boost::unique_lock<boost::mutex> lock(_opEndedMutex);
while(_thereIsOngoingOp)
{
_opEndedCondition.wait(_opEndedMutex);
}
问题在于资源管理。这是一个C ++类,因此当检测到操作结束时,此类的用户可能会删除操作符对象(如果存在任何活动操作,析构函数将等待完成)。可以通过等待或接受通知来检测操作的结束。因此,如果我首先调用_opEndedCondition.notify_all()并且用户删除操作符对象,则在尝试调用OperationEnded()时可能会崩溃,因为m_spNotificationManager已被删除。如果选择首先调用OperationEnded()并且用户在此调用期间删除操作符对象,则在尝试访问_opEndedMutex,_thereIsOngoingOp和_opEndedCondition时可能会崩溃。
作为解决方案,首先想到的是使用新的互斥锁来保护这两个调用。这看起来并不漂亮,因为我无法预见如果引入新的互斥锁会发生什么,并且如果在OperationEnded()通知中用户同步启动新操作。我也不确定如何在wait()方法中使用新的互斥锁。
note1:此API既用于我们公司的公司和其他公司的应用程序。所以我无法摆脱任何一种同步机制。
注意2:我修改了原始代码,变量和方法名称,因此可能存在拼写错误,但这个想法应该是明确的。
EDIT1:
操作员对象保留在通过工厂生成的共享库中,然后通过接口暴露给外部世界。 所以操作符对象的典型生命周期如下:
IOperator * op = factory:getNewOperator();
//perform operations with op
op->Release() //this one goes and deletes the op object
另请注意,操作员对象可以重复使用。客户端代码可以使一个新的操作符对象多次使用它并在最后删除它。
答案 0 :(得分:2)
你有几个解决方案:
您可以从std::enable_shared_from_this专门化您的操作对象。这意味着在客户端代码中,您不再删除该对象,而是将std :: shared_ptr设置为nullptr,并且在实际删除对象时不关心。
您可以在计时器延迟时实施有限的垃圾收集:当您在客户端代码中收到OperationEnded()
通知时,您将获取指针并将其放在队列中,同时将时间戳放在队列中对象被添加。然后队列将有一个活动对象在计时器上唤醒,占用当前时间,如果时间戳(比如说)比当前时间至少早五秒,则删除它。
您可以使用object pool分配和取消分配操作类。当一个对象不再使用时,它实际上不会被删除,而是作为池中的免费(回收)对象放置。在完成处理后,当池被销毁时,实际上将删除该对象。
您可以为对象创建生命周期管理器,删除操作对象,然后发送通知。
您可以在结尾处进行'OperationEnded'功能调用delete this;
;然后,您将实现客户端代码,以便在接收通知时将指针设置为NULL;这样的解决方案虽然很脆弱,但实际上,这可能会将问题转移到另一段代码中。
最后,您可以实现所有这些的自定义组合。
答案 1 :(得分:0)
我们通过计算异步操作来解决这个问题。
构造新的运算符对象时,我们将其指针添加到引用计数表。 当此对象启动异步操作时,我们会增加引用计数和何时 操作结束我们减少引用计数。
如果引用计数为0,则在对象上调用release时,我们将其删除。如果大于0 我们将指针保存在soonToBeDeletables向量中。所以当一个操作结束时,如果引用计数为0,并且如果执行该操作的操作符对象在sooToBeDeletables向量中,则删除Operator对象。
当异步操作结束时,我们首先发出条件变量的信号,然后进行OperationEnded()调用。但可能反过来也会奏效。
通过这种方法,现在可以解决数据竞争,并且删除是确定性的。我们不需要一个后台工作线程,它会在一段时间之后唤醒并进行删除。确定性是必需的,因为当构造,使用(同步)和删除多个对象时,一段时间内,几个对象将同时驻留在存储器中,这在存储器受限(移动)系统中是不希望的。
获得的经验教训:尽可能尝试提供单一的通知机制。