0

我正在使用互斥锁保护指针,以进行已经这样的写入

// 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(pointer) 指针 = 0; 指针 = 另一个指针;原子?

谢谢你。

4

4 回答 4

4

这不安全,“双重检查锁定”也不安全。请务必阅读本文

于 2012-10-23T16:11:32.470 回答
3

这在形式上是非法的,因为您正在引入数据竞赛。而且我不只是在谈论一些微妙的非原子读取,而是非常简单的另一个线程可能已经在你的检查和获取你的锁之间操纵指针。

但是,这里有一种方法可以让您对这种暴行至少感觉好一点:

if (pointer)  // dirty read, eek
{
    boost::mutex::scoped_lock lock(pointer_mutex);
    if (pointer) { pointer = 0; }  // reliable
}
于 2012-10-23T16:05:14.173 回答
1

我会将互斥锁放在块之外。

我不知道 boost::mutex 的内部结构,但我认为它是以理智的方式编写的。一个互斥锁/解锁可以在大约 10 个周期内实现。总是锁定它真的不会是那么大的性能问题。

对于多线程,您真的希望您的系统在 100% 的时间里都是线程安全的。很多时候,在测试中,MT 系统看起来 100% 都运行良好,但使用模式略有不同或负载量不同,问题开始发生。而且 MT 崩溃可能真的很难调试,因为错误可能发生在一个线程中,但会导致另一个线程崩溃。

@edit:您是否希望多个线程能够使用指针,但一次只有一个线程能够更改它?如果是这样,请使用读/写锁。多个线程可以读取,但只有一个线程可以写入,并且会排除读者。

读/写锁的开销比单纯的互斥锁要大一点,但多线程可以“doStuff”这一事实弥补了这一点。并将读写锁放在块外。

于 2012-10-23T16:13:10.987 回答
1

(最初OP在获得互斥锁后没有再次检查thread1中的指针,这意味着它可能已被thread3变为null)。

然而,即使有修复,编译器也有可能过度优化并“缓存”它在检查中看到的值。(双重检查锁定问题)。

关于这个问题,C++11 中会有原子版本。

if( pointer )
    pointer == 0;

不是原子的。指针可以在这些调用之间改变。

这样做的问题

if( pointer )
{
     mutex_lock lock( mutex );
     if( pointer )
     {
         pointer = 0;
     }
}

更多的是告诉编译器不要“优化”并认识到指针可能在你第一次检查它和你第二次检查它之间发生了变化。

您可以通过多种方式尝试智取编译器。最明显的方法是使用volatile关键字,尽管遗憾的是标准并没有强制编译器遵守它。您可以使用返回指针的函数并使该函数成为虚拟函数或类似的东西,以防止编译器内联它。

或者你可以在这种情况下使用汇编的极端路径。

顺便说一句,如果情况需要,请使用boost::once.

于 2012-10-23T16:14:45.083 回答