我正在使用互斥锁来保护指针,因为写入已经像这样
// thread1
if(pointer)
{
boost::mutex::scoped_lock lock(pointer_mutex);
if(pointer)
pointer->DoStuff();
}
// thread2
if(pointer)
{
boost::mutex::scoped_lock lock(pointer_mutex);
if(pointer)
pointer = anotherPointer;
}
// thread3
if(pointer)
{
boost::mutex::scoped_lock lock(pointer_mutex);
pointer = 0;
}
我不想将该互斥量放在块之外,因为指针在99.999的时间内为空。
这样可以正常工作,没有崩溃,但我没有足够的经验证明它是线程安全的。
我的问题是:
if(指针)指针是否为0; pointer = anotherPointer;原子?
谢谢。
答案 0 :(得分:4)
它不安全,也不是“双重锁定”。请执行阅读此paper。
答案 1 :(得分:3)
这是正式的非法,因为你正在引入数据竞赛。而且我不只是在讨论一些微妙的非原子读取,但很简单,另一个线程可能一直在操纵你的支票和获取锁之间的指针。
然而,这是一种让这种暴行至少更好一点的方法:
if (pointer) // dirty read, eek
{
boost::mutex::scoped_lock lock(pointer_mutex);
if (pointer) { pointer = 0; } // reliable
}
答案 2 :(得分:1)
我会将互斥锁放在块之外。
我不知道boost :: mutex的内部结构,但我认为它是以理智的方式编写的。可以在大约10个周期内实现互斥锁定/解锁。总是锁定它确实不会是一个很大的性能问题。
对于多线程,您确实希望您的系统在100%的时间内都是线程安全的。很多时候,在测试中,MT系统看起来在100%的时间内都能正常运行,但是使用模式略有不同或负载问题开始发生。并且MT崩溃可能真的很难调试,因为错误可能发生在一个线程中,但崩溃了另一个线程。
@edit:你的性能问题是你希望多个线程能够使用指针,但是一次只能有一个线程能够改变它吗?如果是这样,请使用读/写锁。多个线程可以读取,但只有一个线程可以写入并将排除读者。
读/写锁的开销比直接的互斥锁略大,但更多的是由多个线程可以“doStuff”组成。并将读写锁定放在块外。
答案 3 :(得分:0)
(原来OP在获取互斥锁之后没有在thread1中再次检查指针,这意味着它可能已被thread3转为null)。
然而,即使使用此修复程序,编译器也有可能过度优化并“缓存”它在检查中看到的值。 (双重检查锁定问题)。
关于这个问题,C ++ 11中将有原子版本。
if( pointer )
pointer == 0;
不是原子的。指针可能会在这些调用之间发生变化。
这样做的问题
if( pointer )
{
mutex_lock lock( mutex );
if( pointer )
{
pointer = 0;
}
}
更多的是告诉编译器不要“优化”并且识别指针在第一次检查时和第二次检查时可能已经发生了变化。
有些方法可以尝试智胜编译器。最明显的方法是使用volatile
关键字,尽管标准遗憾地没有强制编译器遵守它。您可以使用一个返回指针的函数,并将该函数设置为虚拟或类似的函数,以防止编译器将其内联。
或者你可以在这种情况下使用汇编的极端路径。
顺便提一下,如果情况需要,请使用boost::once
。