在使用clang的线程消毒剂时,我们注意到数据争用警告。我们认为这是由于std :: string的copy-on-write技术不是线程安全的,但我们可能是错的。我们减少了对此代码的警告:
void test3() {
std::unique_ptr<std::thread> thread;
{
auto output = make_shared<string>();
std::string str = "test";
thread.reset(new std::thread([str, output]() { *output += str; }));
// The str string now goes out of scope but due to COW
// the captured string may not have the copy of the content yet.
}
thread->join();
}
在启用线程清理程序的情况下编译时:
clang++ -stdlib=libc++ -std=c++11 -O0 -g -fsanitize=thread -lpthread -o test main.cpp
或
clang++ -std=c++11 -O0 -g -fsanitize=thread -lpthread -o test main.cpp
当多次运行时,它最终会产生此警告:
WARNING: ThreadSanitizer: data race (pid=30829)
Write of size 8 at 0x7d0c0000bef8 by thread T62:
#0 operator delete(void*) <null>:0
...
Previous write of size 1 at 0x7d0c0000befd by thread T5:
#0 std::__1::char_traits<char>::assign(char&, char const&) string:639
...
这是线程消毒器的假阳性还是真正的数据竞争?如果以后, 它可以在不改变代码的情况下工作(例如通过将一些标志传递给编译器),这是字符串实现中的已知错误(或其他东西)吗?
更新: clang --version 输出:
Ubuntu clang version 3.5-1ubuntu1 (trunk) (based on LLVM 3.5)
Target: x86_64-pc-linux-gnu
Thread model: posix
更新:The cpp我用来重现此警告。
答案 0 :(得分:2)
[edit]下面的假设结果是错误的,请参阅评论中的链接。 T5,而不是T62是上面代码中产生的线程。
<击> 理解线程ID是有用的,但我假设T5是主线程,T62是生成线程。看起来副本是在主线程上(在新线程被spwaned之前)并在新线程上销毁(显然)。这是安全的,因为新线程在存在之前不能与主线程竞争。 击>
<击> 因此,这是一个线程清理程序错误。它无法检查上一次写入时是否存在线程T62。
击>
答案 1 :(得分:1)
这非常棘手。我在下面的代码中总结了逻辑:
In thread T62: Create string s (with reference count) Create output_1 pointing to s in the thread storage for T62 Create thread T5 Create output_2 pointing to s in the thread storage for T5 Sync point In thread T5: Append to s ** MODIFY ** Thread-safe decrement of reference count for s (not a sync point) End of output_2 lifetime Exit In thread T62: Thread-safe decrement of reference count for s (not a sync point) End of output_1 lifetime Deallocate s ** MODIFY ** Join Sync point In thread T62: Destroy T5
据我所知,该标准不保证调用shared_ptr
删除器的同步:
(20.8.2.2/4)为了确定是否存在数据争用,成员函数只能访问和修改shared_ptr和weak_ptr对象本身而不是它们所引用的对象。
我认为这意味着在调用shared_ptr的成员函数时实际发生在指向对象上的任何修改,例如删除者可能做出的任何修改,都被认为超出了{{{ 1}},因此shared_ptr
不负责确保他们不引入数据竞争。例如,在T62尝试销毁它时,T62对字符串所做的修改可能对T62不可见。
然而,Herb Sutter在他的“Atomic&lt;&gt;武器”谈话中表示,他认为这是一个错误,导致shared_ptr
析构函数中的引用计数的原子减量没有获取和释放语义,但我不确定它是如何违反标准的。