C++ 三十部分库中的线程安全

C++ 三十部分库中的线程安全,c++,multithreading,thread-safety,C++,Multithreading,Thread Safety,我想使用其他人开发的库,其中我只有库文件,没有源代码。我的问题是:库提供了一个具有许多功能的类。类本身不是线程安全的。我想让它线程安全,我想知道这段代码是否有效 // suppose libobj is the class provided by the library class my_libobj : public libobj { // code }; 这仅从libobj继承,这可能有效,也可能无效,这取决于类是否为继承而设计,至少有一个虚拟析构函数 在任何情况下,它都不会免费为您

我想使用其他人开发的库,其中我只有库文件,没有源代码。我的问题是:库提供了一个具有许多功能的类。类本身不是线程安全的。我想让它线程安全,我想知道这段代码是否有效

// suppose libobj is the class provided by the library
class my_libobj : public libobj {
   // code
};
这仅从libobj继承,这可能有效,也可能无效,这取决于类是否为继承而设计,至少有一个虚拟析构函数

在任何情况下,它都不会免费为您购买线程安全性。最简单的方法是在类中添加互斥体,并在进入关键部分时锁定互斥体:

class my_obj {
    libobj obj;  // inheritance *might* work too
    boost::mutex mtx;

    void critical_op()
    {
        boost::unique_lock lck(mtx);
        obj.critical_op();
    }
};

这是一个非常粗粒度的设计,只有一个互斥;如果您了解各种操作的行为,您可能会使它更细粒度。正如@dribeas所解释的那样,它也不是傻瓜式的。

如果你不满足于序列化对它的所有调用,那么不知道它是如何实现的,那么在一个没有被设计的库中改装线程安全性——顺便说一句,有不同的级别——可能是不可能的,即使如此,如果接口足够糟糕,也会有问题——例如,请参阅strtok。

如果不知道至少类的实际接口,这是不可能回答的。一般来说,答案是否定的

从实际C++的观点来看,如果类没有被设计为扩展,则每个非虚拟方法都不会被重写,因此,你可能会得到一些线程安全和一些非线程安全方法的混合。 即使您决定在不继承的情况下进行包装,并且仅在持有锁的情况下强制委派,该方法仍然不是在所有情况下都有效。线程安全不仅需要锁定,还需要一个可以使线程安全的接口

以STL中的堆栈实现为例,只需添加一层锁定,即使每个方法都是线程安全的,就不能保证容器上的线程安全。考虑几个线程向堆栈中添加元素和两个线程拉动信息:

if ( !stack.empty() ) {  // 1
   x = stack.top();      // 2
   stack.pop();          // 3
   // operate on data
}
这里有很多可能出错的地方:当堆栈只有一个元素时,两个线程都可能执行测试[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操作中等待一个条件,该条件通过使用条件变量或类似的东西保证锁定,直到数据准备就绪……

最简单的解决方案是为该库创建唯一的单线程,并且只能从该线程访问库,使用消息队列传递请求和返回参数。

您不是指第三方库吗?