空指针检查线程安全

时间:2012-10-23 16:03:14

标签: c++ multithreading pointers thread-safety

我正在使用互斥锁来保护指针,因为写入已经像这样

// 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;原子?

谢谢。

4 个答案:

答案 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