Mer*_*ert 7 c++ multithreading thread-safety
我有一个类,通过它我可以异步提供一些服务(同样的调用也可以同步进行).当请求时,此类的对象(比如运算符)在另一个线程中启动操作.其他对象可以注册到操作符对象的通知,以便在操作结束时对此对象调用OperationEnded()方法.其他对象也可以通过调用操作符对象上的Wait()来等待此操作的完成.
操作结束时的代码大致如下:
_opEndedMutex.lock();
_thereIsOngoingOp = false;
_opEndedCondition.notify_all();
_opEndedMutex.unlock();
//no more call after notification
m_spNotificationManager->OperationEnded();
Run Code Online (Sandbox Code Playgroud)
而wait()函数如下:
boost::unique_lock<boost::mutex> lock(_opEndedMutex);
while(_thereIsOngoingOp)
{
_opEndedCondition.wait(_opEndedMutex);
}
Run Code Online (Sandbox Code Playgroud)
问题在于资源管理.这是一个C++类,因此当检测到操作结束时,此类的用户可能会删除操作符对象(如果存在任何活动操作,析构函数将等待完成).可以通过等待或接受通知来检测操作的结束.因此,如果我首先调用_opEndedCondition.notify_all()并且用户删除操作符对象,则在尝试调用OperationEnded()时可能会崩溃,因为m_spNotificationManager已被删除.如果选择首先调用OperationEnded()并且用户在此调用期间删除操作符对象,则在尝试访问_opEndedMutex,_thereIsOngoingOp和_opEndedCondition时可能会崩溃.
我想到的第一件事就是使用新的互斥锁来保护这两个调用.这看起来并不漂亮,因为我无法预见如果引入新的互斥锁会发生什么,并且如果在OperationEnded()通知中用户同步启动新操作.我也不确定如何在wait()方法中使用新的互斥锁.
注1:此API用于我们自己公司和其他公司的应用程序.所以我无法摆脱任何一种同步机制.
注意2:我修改了原始代码,变量和方法名称,因此可能存在拼写错误,但这个想法应该是明确的.
EDIT1:
操作员对象保留在通过工厂生成的共享库中,然后通过接口暴露给外部世界.所以操作符对象的典型生命周期如下:
IOperator * op = factory:getNewOperator();
//perform operations with op
op->Release() //this one goes and deletes the op object
Run Code Online (Sandbox Code Playgroud)
另请注意,运算符对象是可重用的.客户端代码可以使新的操作符对象多次使用它并在最后删除它.
我们通过计算异步操作来解决这个问题。
当构造一个新的运算符对象时,我们将其指针添加到引用计数表中。当该对象开始异步操作时,我们会增加引用计数,而当操作结束时,我们会减少引用计数。
当对对象调用release时,如果引用计数为0,我们将删除它。如果它大于 0,我们将指针保留在 SoonToBeDeletables 向量中。因此,当操作结束时,如果引用计数为 0,并且执行该操作的运算符对象位于 sooToBeDeletables 向量中,我们将删除该运算符对象。
当异步操作结束时,我们首先向条件变量发出信号,然后调用OperationEnded()。但也许反过来也能起作用。
通过这种方法,数据争用现已得到解决,并且删除是确定性的。我们不需要后台工作线程在一段时间后醒来并进行删除。需要确定性,因为当多个对象因此被构造、使用(同步)和删除时,在一段时间内,多个对象将同时驻留在内存中,这在内存有限(移动)系统中是不希望的。
经验教训:尽可能尝试仅提供单一通知机制。