多线程使用用户定义的对象

时间:2011-10-21 09:06:26

标签: c++ stl thread-safety

我对使用多个线程的对象有疑问。

首先,保护对象不被std::lock_guard或其他人同时访问是没有问题的。

下一步是使用volatile声明对象。

class A
{
   public: int val;
};

volatile A a;

但这样做最终会产生许多具有类型限定符volatile的新函数     ...     int GetVal()volatile;     void SetVal()volatile;     ...

好的,一切正常。

但是如何访问stl成员,例如std::vectorstd::map.

如果我想要一个不稳定的std::vector<int>我运行了很多错误,而stl没有定义任何volatile方法。

从这一点开始,我在网上搜索了很多“技巧”。在后台主要有相同的想法:拥有一个用于并发保护的互斥锁,并使用volatile移动const_cast以使标准接口可用。

作为此实施的示例:

template <typename T>
class PseudoPtr {
    public:
        PseudoPtr(volatile T& obj, mutex& mtx) 
            : guard(mtx), 
              pObj_(const_cast<T*>(&obj)) 
        { }   
        ~PseudoPtr() { }   
        // Pointer behaviour
        T& operator*()  {   return *pObj_;  }   
        T* operator->() {   return  pObj_;  }   

    private:
        my_guard<mutex> guard;
        T* pObj_;
        PseudoPtr(const PseudoPtr&) = delete;
        PseudoPtr& operator=(PseudoPtr &) = delete;
};

在没有volatile限定符的情况下从上面获取我的示例类:

 class A
 {
     ...
     int GetVal();
 };

但如果我以下列方式使用它:

 volatile A a;
 mutex mu;

 ...
 for (;;) 
 {
     PseudoPtr ptr(a, mu); //starts locking the object
     if (ptr->GetVal())
     {
         ... do something ...
     }
 }

我永远不会在对象a中看到任何变化,因为编译器可以进行优化 访问离开,因为volatile对象是const_cast,因此优化器没有 了解不稳定行为。

对于我自己的所有类,这不是问题,而我可以编写所有volatile方法。但是如果我想使用stl容器,就没有办法从它们那里获得volatile实例并通过const_cast使用它。

好的,实际的编译器并不是那么难以优化(gcc 4.6.1),但有保证编译器永远不会进行这种优化吗? const_cast会破坏volatile而不会const_cast volatile stl对象无效,而stl容器根本没有易失性方法。

当我思考时,网上发现的所有“技巧”都是错误的,因为他们都忽略了 优化

这在我的问题中运行:

  • 我对volatile和optimizers的解释错了吗?
  • 是否有使用stl conatiners线程安全的“标准”工作解决方案?

----------------------经过几个小时的阅读------------------- -----

首先,是的,pthread_mutex_lock应该执行以下操作:*访问内存原子(这是我之前唯一知道的事情)*保证内存对所有其他线程可见

好的,第二个对我来说是新手!这导致了下一个问题:这个技巧如何运作?

提供互斥语义的库必须有机会告诉*编译器,停止乱序执行*将所有缓存(寄存器优化)变量写入硬件*告诉硬件同步所有硬件缓存等< / p> 好的,好的!但是如何工作:特定于gcc的实现:存在一个名为barrier()的宏,类似于

asm volatile(“”:::“memory”);

gcc的pthread库包含在glibc / nptl中,每个保证数据可见性到其他线程的函数只调用barrier宏或直接调用内联汇编程序或者用类似的东西做事情。

如果再没有误会,这就是幕后的简单事情。

我学到的东西:在任何情况下,与互斥锁定相结合的volatile都不是很完整。

我写下这个答案,希望其他人能够在这个神秘面前变得更加沉闷。

如果再次出现错误和误解?!:请告诉我!

感谢您的所有答案!

(很抱歉编辑我的帖子以添加我自己的结论,但我无法在帖子中添加自己的答案)

3 个答案:

答案 0 :(得分:4)

如果您使用互斥锁等喜欢同步对象的访问权限,you don't need volatile at all

答案 1 :(得分:2)

使数据访问线程安全的“标准”方法是使用互斥锁 Volatile只是告诉你这个变量可能会在你的应用程序之外发生变化,因此编译器无法根据对其值的假设进行任何优化。

如果要使类具有线程安全性,唯一的方法是仔细评估所有成员,并设计互斥锁,以确保所有数据访问都具有所需的一致性级别;请注意,作为pointed out by Nikko,STL已经对读取数据做了一些保证。

最后,如果你可以切换到C ++ 11(或C ++ 0x),那么你可以使用它的所有threading facilities,包括线程安全的容器。所有较新的编译器版本都对此标准有一些支持,因此除非您在某些旧版环境中被强制使用,否则这是您可能需要考虑的选项。

答案 2 :(得分:2)

  

我对volatile和optimizers的解释错了吗?

是的,你错了,你不需要挥发性。这不是Java。您需要使用互斥锁(或最新的线程工具)保护共享对象。据我所知,C语中的“volatile”并不是出于思路。

  

是否存在使用stl conatiner线程安全的“标准”工作解决方案?

STL容器是线程独立的,这意味着同时读取不需要被互斥锁保护,但如果你正在编写它必须是。