2012-07-14 182 views
19

是unique_ptr线程安全吗?下面的代码不可能两次打印相同的数字吗?是unique_ptr线程安全吗?

#include <memory> 
#include <string> 
#include <thread> 
#include <cstdio> 

using namespace std; 

int main() 
{ 
    unique_ptr<int> work; 

    thread t1([&] { 
     while (true) { 
      const unique_ptr<int> localWork = move(work); 
      if (localWork) 
       printf("thread1: %d\n", *localWork); 
      this_thread::yield(); 
     } 
    }); 

    thread t2([&] { 
     while (true) { 
      const unique_ptr<int> localWork = move(work); 
      if (localWork) 
       printf("thread2: %d\n", *localWork); 
      this_thread::yield(); 
     } 
    }); 

    for (int i = 0; ; i++) { 
     work.reset(new int(i)); 

     while (work) 
      this_thread::yield(); 
    } 

    return 0; 
} 

回答

16

不,它不是线程安全被读取。

这两个线程都可能是move工作指针没有明确的同步,所以两个线程都可能得到相同的值,或者两者都可能得到一些无效的指针......这是未定义的行为。

如果你想要做这样的事情正确,你可能需要使用像这样std::atomic_exchange两个线程可以读取/修改共享工作指针与正确的语义。

+0

“令人兴奋的是,当如果发生这种情况,它也将导致整的双重释放。”它可能*。它可能不会。它可能导致根本不释放该整数。它可能会导致两个移动的版本都有指针值的一半。它可以做各种事情。 – 2012-07-14 17:29:23

+0

的确,我对这个架构做了一些假设,而这些架构并没有真正的保证。 – Useless 2012-07-14 19:05:26

7

根据Msdn

以下线程安全规则适用于标准 C++库中的所有类(除shared_ptr的和iostream类,如下所述 )。

单个对象是线程安全从多个线程读取。例如,对于给定对象A的 示例,可以同时从线程2读取线程1的A和线程 以安全。

如果单个对象正在被一个线程写入,则全部读取到 并写入该对象上的相同或其他线程必须被 保护。例如,给定对象A,如果线程1正在写入 A,则线程2必须被阻止读取或写入A.

即使读取和写入类型的一个实例也是安全的另一个 线程正在读取或写入相同类型的不同实例。 例如,给定对象A和相同类型的B,它是安全的,如果A 正被写入线程1和B在螺纹2.

28

的unique_ptr是线程安全的正确使用。你破坏了不成文的规则:你不应该通过引用在线程之间传递unique_ptr。

背后的unique_ptr的理念是,它在任何时候都一个单一的(唯一的)的所有者。因此,您可以始终在线程之间安全地传递它,而无需同步 - 但您必须通过值来传递它,而不是通过引用来传递它。一旦您创建别名到unique_ptr,您将失去唯一性属性,并且所有投注都将关闭。不幸的是,C++不能保证唯一性,所以你留下了一个你必须遵循的约定。不要为unique_ptr创建别名!

+0

有趣的观察。我试着去思考什么时候通过引用来传递unique_ptr是有意义的。我的意思是当你不想通过所有权的情况下。 – Ghita 2012-07-14 18:51:16

+0

如果'在线程之间传递'意味着使用unique_ptr参数启动一个新线程,那么您可以将unique_ptr替换为几乎任何类型,并仍然将其称为线程安全。任何东西是正确使用线程安全:) – 2012-08-04 15:43:32

+1

你确定吗?我的内心偏执的程序员告诉我,如果你在线程之间传递它是UB,因为你不确保seq_cst,也就是说,如果最后指向的是由线程1修改,然后向上传递给线程2,那么线程2可能会看到过时的值指向东西。 – NoSenseEtAl 2012-12-31 08:48:40