多线程使用用户定义的对象
multi threaded use of user defined objects
我对使用多个线程中的对象有疑问。
首先,保护对象不受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>
,我在很多错误中运行,而仍然不定义任何易失性方法。
从这一点我搜索了网络,发现了很多"技巧"。在后台基本上是一样的想法:用互斥锁来保护并发性,用const_cast
把volatile
移开,使标准接口可用。
作为这个实现的一个示例:
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
将打破volatile
和const_cast
在volatile
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容器是线程独立的,这意味着同时读取不需要被互斥锁保护,但如果你正在写,它必须被保护。
- QML:修改在不同QML文件(而非main.QML)中定义的子对象的属性
- 自定义对象的dlib序列化在gcc中失败
- 已定义/多个对象定义/可视化工作室
- 如何将对象定义为一种类型,然后再将其声明为子类型
- 使用派生类实例化基类,而不在对象定义中使用指针
- 为非标准对象定义散列函数和相等函数
- 如何以对象定义向多个构造函数发送值
- C++"更大"的函数对象定义
- C++宏整数和对象定义
- 外部未命名结构对象定义
- 无法编译.头文件.包含自己的对象定义
- 在C++中,std::cin 对象定义在哪里
- 是否应该为具有指向其父对象的指针的对象定义一个复制构造函数
- 为每个对象定义一个唯一的函数
- 我可以为某个对象定义一个类方法吗?
- 如何为对象定义模板化方法,其中一些对象也是模板化的
- 在游戏对象定义中放置的位置
- Opencv3最小对象定义错误
- 在对象定义之后调用构造函数
- 在 c++ 中为对对象定义排序函数的比较器