三十部分图书馆的线程安全

时间:2011-04-18 07:10:11

标签: c++ multithreading thread-safety


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

// suppose libobj is the class provided by the library
class my_libobj : public libobj {
   // code
};

4 个答案:

答案 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)

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