代码之家  ›  专栏  ›  技术社区  ›  Jos v E

从std::set insert()和find()写写数据竞赛?

  •  4
  • Jos v E  · 技术社区  · 12 年前

    为了试验线程清理器,我创建了一个小型C++程序,目的是包含数据竞赛。事实上,tsan确实检测到了错误,太棒了!然而,我对生成的消息感到困惑。。。

    1. 它报告了 写-写 数据竞赛,我本以为 读写的 比赛我希望 find() 不在我的容器中写入。如果我做了进一步的小代码调整,试图获得 const 的版本 set::find() ,同样的写写竞争似乎仍然存在。
    2. 它显示4字节之间的写入冲突 原子的 写入和在同一地址的8字节写入。容器类中的同一个字段被两种不同的访问类型访问似乎很奇怪。

    是否有使用常量的选项 find() 不写入STL容器?

    这是经过测试的C++程序:

    /*****************************************************************************
     * Small example with an inter-thread data race that is not obvious.
     * the error is a consequence of the non-threadsafeness of the STL containers.
     * Threading is created through portable C++11 constructs.
     * Tsan does detect the data race(?).
     *
     * Compile with one of:
     * g++-4.8 -std=c++11 -g -Wall -o race-stl11b race-stl11b.cc -pthread
     * g++-4.8 -std=c++11 -g -Wall -fsanitize=thread -fPIE -o race-stl11b-tsan race-stl11b.cc -ltsan -pie -pthread
     ******************************************************************************/
    
    #include <iostream>
    #include <thread>
    #include <set>
    
    int main()
    {
        // create an empty bucket
        std::set<int> bucket;
    
        // Use a background task to insert value '5' in the bucket 
        std::thread t([&](){ bucket.insert(5); });
    
        // Check if value '3' is in the bucket (not expected :-)
        bool contains3 = bucket.find(3) != bucket.cend();
        std::cout << "Foreground find done: " << contains3 << std::endl;
    
        // Wait for the background thread to finish
        t.join();
    
        // verify that value '5' did arrive in the bucket
        bool contains5 = bucket.find(5) != bucket.cend();
        std::cout << "Background insert: " << contains5 << std::endl;
    
        return 0;
    }
    

    这是tsan输出的一部分:

    WARNING: ThreadSanitizer: data race (pid=21774)                                                                                               
    
      Write of size 8 at 0x7d080000bfc8 by thread T1:                                                                                             
        #0 <null> <null>:0 (libtsan.so.0+0x00000001e2c0)                                                                                          
        #1 deallocate /usr/include/c++/4.8/ext/new_allocator.h:110 (exe+0x000000002a79)                                                           
        #2 deallocate /usr/include/c++/4.8/bits/alloc_traits.h:377 (exe+0x000000002962)                                                           
        #3 _M_destroy /usr/include/c++/4.8/bits/shared_ptr_base.h:417 (exe+0x00000000306b)                                                        
        #4 <null> <null>:0 (libstdc++.so.6+0x0000000b5f8a)                                                                                        
    
      Previous atomic write of size 4 at 0x7d080000bfc8 by main thread:
        #0 <null> <null>:0 (libtsan.so.0+0x00000000da45)
        #1 __exchange_and_add /usr/include/c++/4.8/ext/atomicity.h:49 (exe+0x000000001c9f)
        #2 __exchange_and_add_dispatch /usr/include/c++/4.8/ext/atomicity.h:82 (exe+0x000000001d56)
        #3 std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() /usr/include/c++/4.8/bits/shared_ptr_base.h:141 (exe+0x00000000390d)
        #4 std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() /usr/include/c++/4.8/bits/shared_ptr_base.h:553 (exe+0x00000000363c)
        #5 std::__shared_ptr<std::thread::_Impl_base, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr() /usr/include/c++/4.8/bits/shared_ptr_base.h:810
     (exe+0x00000000351b)
        #6 std::shared_ptr<std::thread::_Impl_base>::~shared_ptr() /usr/include/c++/4.8/bits/shared_ptr.h:93 (exe+0x000000003547)
        #7 thread<main()::__lambda0> /usr/include/c++/4.8/thread:135 (exe+0x0000000020c3)
        #8 main /home/......./race-stl11b.cc:22 (exe+0x000000001e38)
    

    感谢您的反馈, 乔斯

    1 回复  |  直到 12 年前
        1
  •  1
  •   Klaas van Gend    12 年前

    看起来ThreadAnitizer对std::thread实现给出了一个假肯定。

    将示例简化为不执行任何集合操作,如下所示:

    #include <iostream>
    #include <thread>
    #include <set>
    
    int main()
    {
        std::set<int> bucket;
        std::thread t([&](){ /*bucket.insert(5);*/ });
        t.join();
    
        return 0;
    }
    

    在ThreadAnitizer中仍然会出现相同的错误。

    注意ThreadAnitizer 不是 找到您的读写竞争条件。