通过shared_ptr同步:ThreadSanitzier误报?

时间:2017-03-22 11:33:35

标签: c++ multithreading shared-ptr thread-sanitizer

以下代码通过shared_ptr进行同步:

#include <memory>
#include <thread>
#include <future>
#include <chrono>
#include <cassert>
#include <atomic>

using std::shared_ptr;
using std::async;
using std::launch;
using std::this_thread::sleep_for;
using namespace std::literals;

void f1(shared_ptr<int> p)
{
    sleep_for(50ms); // make sure the other has started
    assert(*p == 42);
    p.reset();
    sleep_for(50ms); // make sure the other has deleted *p
}

void f2(shared_ptr<int> p)
{
    while (p.use_count() != 1)
    {
        sleep_for(1ms);
    }
    p.reset();
    sleep_for(50ms);
}

int main()
{
    shared_ptr<int> p(new int(42));
    auto t1 = async(launch::async, f1, p);
    auto t2 = async(launch::async, f2, p);

    p.reset();

    t1.get();
    t2.get();

    return 0;
}

我用

编译它
clang++-4.0 -std=c++1z -stdlib=libc++ -Wall -g -O3 -march=native -fsanitize=thread -fno-omit-frame-pointer -pthread sharedPtr.cc -o sharedPtr

运行时,ThreadSanitizer会给我以下问题:

==================
WARNING: ThreadSanitizer: data race (pid=273)
  Write of size 8 at 0x7b0400000000 by thread T2:
    #0 operator delete(void*) ??:? (sharedPtr+0x4b4af1)
    #1 std::__1::default_delete<int>::operator()(int*) const /usr/include/c++/v1/memory:2516 (discriminator 1) (sharedPtr+0x4b74d8)
    #2 std::__1::__shared_ptr_pointer<int*, std::__1::default_delete<int>, std::__1::allocator<int> >::__on_zero_shared() /usr/include/c++/v1/memory:3759 (discriminator 1) (sharedPtr+0x4b74d8)
[...]

  Previous read of size 4 at 0x7b0400000000 by thread T1:
    #0 f1(std::__1::shared_ptr<int>) /home/dv/src/git/c++-concurrency/test/sharedPtr.cc:22 (sharedPtr+0x4b6fca)
    #1 _ZNSt3__18__invokeIPFvNS_10shared_ptrIiEEEJS2_EEEDTclclsr3std3__1E7forwardIT_Efp_Espclsr3std3__1E7forwardIT0_Efp0_EEEOS5_DpOS6_ /usr/include/c++/v1/__functional_base:415 (sharedPtr+0x4b78cf)
[...]

我假设C ++通过shared_ptr引用计数保证足够的同步,读取shared_ptr永远不会与删除器竞争(对于不同的shared_ptr对象)。而且我希望这是非常常见的用法,所以我很惊讶ThreadSanitizer抱怨这个。

所以这是我的问题:

  1. 我的使用是否安全(我对shared_ptr同步的假设是否正确)? (我希望答案是肯定的,所以现在按照我的真实问题:)
  2. libc ++是否正确实现了同步?
  3. ThreadSanitizer是否真的没有通过引用计数看到同步?

1 个答案:

答案 0 :(得分:3)

  

我的使用是否安全(我对shared_ptr同步的假设是否正确)?

我在use_count()的实现中没有看到需要内存栅栏的标准中的任何内容。 cppreference表示大多数实现使用memory_order_relaxed进行读取,因此无法保证排序。

引用:“在多线程环境中,use_count返回的值是近似值(典型实现使用memory_order_relaxed加载)”

因此,严格来说,我不认为使用use_count()作为信号量是安全的,因为它依赖于对实现的假设。

  

libc ++是否正确实现了同步?

  

ThreadSanitizer是否真的没有通过引用计数看到同步?

我认为ThreadSanitizer正在呼唤你。