我对使用多个线程的对象有疑问。
首先,保护对象不被std::lock_guard
或其他人同时访问是没有问题的。
下一步是使用volatile声明对象。
class A
{
public: int val;
};
volatile A a;
但这样做最终会产生许多具有类型限定符volatile
的新函数
...
int GetVal()volatile;
void SetVal()volatile;
...
好的,一切正常。
但是如何访问stl成员,例如std::vector
或std::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容器根本没有易失性方法。
当我思考时,网上发现的所有“技巧”都是错误的,因为他们都忽略了 优化
这在我的问题中运行:
----------------------经过几个小时的阅读------------------- -----
首先,是的,pthread_mutex_lock应该执行以下操作:*访问内存原子(这是我之前唯一知道的事情)*保证内存对所有其他线程可见
好的,第二个对我来说是新手!这导致了下一个问题:这个技巧如何运作?
提供互斥语义的库必须有机会告诉*编译器,停止乱序执行*将所有缓存(寄存器优化)变量写入硬件*告诉硬件同步所有硬件缓存等< / p> 好的,好的!但是如何工作:特定于gcc的实现:存在一个名为barrier()的宏,类似于
asm volatile(“”:::“memory”);
gcc的pthread库包含在glibc / nptl中,每个保证数据可见性到其他线程的函数只调用barrier宏或直接调用内联汇编程序或者用类似的东西做事情。
如果再没有误会,这就是幕后的简单事情。
我学到的东西:在任何情况下,与互斥锁定相结合的volatile都不是很完整。
我写下这个答案,希望其他人能够在这个神秘面前变得更加沉闷。
如果再次出现错误和误解?!:请告诉我!
感谢您的所有答案!
(很抱歉编辑我的帖子以添加我自己的结论,但我无法在帖子中添加自己的答案)
答案 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容器是线程独立的,这意味着同时读取不需要被互斥锁保护,但如果你正在编写它必须是。