我想使用由其他人开发的库,其中我只有库文件,而不是源代码。我的问题是:图书馆提供了一个具有许多功能的类。类本身不是线程安全的。我想让它的线程安全,我想知道这段代码是否有效
// suppose libobj is the class provided by the library
class my_libobj : public libobj {
// code
};
答案 0 :(得分:3)
这只继承自libobj
,根据该类是否设计用于继承(至少有一个virtual
析构函数),它可能“工作”,也可能不“工作”。
无论如何,它不会免费为您购买线程安全。最简单的方法是在类中添加互斥锁并在进入关键部分时锁定它们:
class my_obj {
libobj obj; // inheritance *might* work too
boost::mutex mtx;
void critical_op()
{
boost::unique_lock lck(mtx);
obj.critical_op();
}
};
(这是一种非常粗糙的设计,只有一个互斥锁;如果你知道各种操作的行为,你可以使它更细粒度。它也不是万无一失的,正如@dribeas所解释的那样。)
答案 1 :(得分:2)
改进线程安全 - 和顺便说一下,有一个不同的级别 - 在一个没有设计的库中可能是不可能的,如果你不满足于只是序列化所有调用它,即使这样,如果界面足够糟糕也可能会出现问题 - 例如,请参阅strtok。
答案 2 :(得分:1)
如果不知道至少类的实际接口,则无法回答。一般来说,答案是否定的。
从实际的C ++角度来看,如果该类不是为了扩展而设计的,那么每个非虚方法都不会被覆盖,因此你最终会混淆一些线程安全和一些非线程安全的方法。
即使您决定换行(没有继承)并仅在持有锁时强制委派,该方法在所有情况下仍然无效。线程安全不仅需要锁定,还需要一个可以使线程安全的接口。
考虑在STL中的stack
实现,只需添加一层锁定(即使每个方法线程安全,你不能保证容器上的线程安全。考虑一些线程向堆栈添加元素两个线程拉信息:
if ( !stack.empty() ) { // 1
x = stack.top(); // 2
stack.pop(); // 3
// operate on data
}
这里有许多可能出错的事情:当堆栈有一个元素然后顺序输入时,两个线程都可能执行test [1],在这种情况下,第二个线程将在[2]中失败(或者获得相同的值并在[3]中失败,即使在容器中有多个对象的情况下,两个线程都可以在执行[3]之前执行[1]和[2],在这种情况下两个线程将使用相同的对象,并且堆栈中的第二个元素将被丢弃而不进行处理......
线程安全需要(在大多数情况下)对API的更改,在上面的示例中,提供bool pop( T& v );
的接口实现为:
bool stack::try_pop( T& v ) { // argument by reference, to provide exception safety
std::lock<std:mutex> l(m);
if ( s.empty() ) return false; // someone consumed all data, return failure
v = s.top(); // top and pop are executed "atomically"
s.pop();
return true; // we did consume one datum
}
当然还有其他方法,你可能不会返回失败,而是等待pop
操作中的条件,该操作通过使用条件变量或类似的东西保证锁定直到数据准备就绪。 ..
答案 3 :(得分:1)
最简单的解决方案是为该库唯一创建一个线程,并且只使用消息队列传递请求和返回参数,从该线程访问该库。