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

multi threaded use of user defined objects

本文关键字:对象 定义 用户 多线程      更新时间:2023-10-16

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

首先,保护对象不受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>,我在很多错误中运行,而仍然不定义任何易失性方法。

从这一点我搜索了网络,发现了很多"技巧"。在后台基本上是一样的想法:用互斥锁来保护并发性,用const_castvolatile移开,使标准接口可用。

作为这个实现的一个示例:

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的任何变化,因为编译器可以优化因为易失性对象是const_cast,所以优化器没有访问了解volatile行为

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

好吧,实际的编译器并没有那么难优化(gcc 4.6.1),但是有一个保证编译器永远不会做这个优化吗?const_cast将打破volatileconst_castvolatile stl对象将不工作,而stl容器没有volatile方法。

认为,所有在网上找到的"技巧"都是有bug的,因为它们都忽略了优化器。

这在我的问题中运行:

  • 我对挥发性和优化器的解释是错误的?
  • 是否有使用stl容器线程安全的"标准"工作解决方案?

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

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

好的,第二个对我来说是新的!这就引出了下一个问题:这个把戏是如何运作的?

提供互斥锁语义的库必须有机会告诉*编译器,停止乱序执行*将所有缓存(寄存器优化)变量写入硬件*告诉硬件同步所有硬件缓存等。

好的,好!具体是如何实现的:gcc特有的实现:存在一个名为barrier()的宏,类似于

asm volatile(":: "memory");

gcc的pthread库包含在glibc/nptl中,每个保证数据对其他线程可见的函数只需调用barrier宏或直接调用内联汇编器或使用类似的方法。

如果没有误解,那就是幕后的简单事情。

我所学到的:volatile和互斥锁在任何情况下都不是全感的。

我写下这个答案是希望其他人也能在这个谜团中变得不那么困惑。

如果再出现错误和误解?!让我知道!

谢谢你的回答!

(抱歉编辑我的帖子添加我自己的结论,但我不能添加自己的答案到我的帖子)

如果你正在使用互斥锁和喜欢同步访问你的对象,你不需要volatile

使数据访问线程安全的"标准"方法是使用互斥锁。
Volatile只是告诉你这个变量可能会在你的应用程序之外改变,所以编译器不能基于对它的值的假设来执行任何优化。

如果你想让你的类线程安全,唯一的方法是仔细评估所有成员,并设计互斥锁,以确保所有数据访问具有所需的一致性级别;请注意,正如Nikko指出的那样,STL已经对读取数据做了一些保证。

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

我对volatile和优化器的解释是错误的?

是的,你错了,你不需要volatile。它不是Java。您需要使用互斥锁(或最新的线程工具)来保护共享对象。据我所知,C语言中的"volatile"并没有考虑到线程。

是否有使用stl容器线程安全的"标准"工作解决方案?

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