【问题标题】:Storing a Map in a TLS - Thread Safety in C++在 TLS 中存储地图 - C++ 中的线程安全
【发布时间】:2015-10-01 04:08:37
【问题描述】:

基本上一个map不能被两个不同的线程同时添加/删除,所以需要同步。

而且我知道 c++ 中的线程本地存储概念是线程安全的,可以被许多线程访问。

    std::map<int,int> * pTemp= (std::map<int,int> *) TlsGetValue(sTlsIndex);
    if (pTemp== NULL)
    {
        pTemp= new std::map<int,int>;
        TlsSetValue(sTlsIndex, pTemp);
    }

如果我将地图放在 TLS 中并从不同的子线程添加/删除/修改地图访问它,那么它应该同步吗?

由于 TLS 已经同步并且我的地图在其中。

【问题讨论】:

    标签: c++ multithreading dictionary thread-local-storage


    【解决方案1】:

    "线程安全,可以被多个线程访问"

    您误解了线程本地存储 - 它透明地为每个线程创建不同的对象,以便该线程可以轻松/快速地找到它们并安全地使用它们尽管不是线程安全的

    如果您希望多个线程访问同一个映射,则不应将其放在线程本地存储中,而应使用std::mutex 或类似的同步原语来控制访问和更新。

    【讨论】:

      【解决方案2】:

      您似乎没有掌握线程本地存储的概念。它不会同步对同一个对象的访问——相反,它保证每个线程都有自己的版本,因此多个线程不会访问同一个对象,除非他们特别尝试这样做,例如,传递一个指向从一个线程到另一个线程的线程本地对象的指针。除非非常罕见地传递这些指针,否则不会出现线程不安全问题。

      它主要(我什至敢说只是)与静态变量结合使用。根据定义,在堆栈上创建的自动变量是线程安全的,因为始终只有一个线程使用此堆栈。

      让我用一些例子来澄清一下:

      typedef std::map<int, int> my_map_t;
      my_map_t getmap() {
          static my_map_t my_map = { {10, 20}, {30, 40}, {50, 60} };
      
          return my_map;
      }
      
      void func1() {
          getmap()[70] = 80;
      }
      void func2() {
          getmap()[90] = 100;
      }
      
      ...
      std::thread thr1(func1);
      std::thread thr2(func2); 
      

      在上面的示例中,func1() 在 thr1 中执行和 func2() 在 thr2 中执行将访问同一个 map 实例,并将在其上同时执行插入(当然可能),从而导致未定义的行为。此代码后的单张地图状态无法预测。

      但是,如果getmap()的代码被如下替换:

      my_map_t getmap() {
          static _Thread_local my_map_t my_map = { {10, 20}, {30, 40}, {50, 60} };
      
          return my_map;
      }
      

      每个线程都有它自己的地图副本。程序中将有两个映射实例,只有一个元素不同 - 一个将有(除了公共元素之外)一对​​ (90, 100),另一个将有一对 (70, 80) )。这两张图的状态是确定的,这里没有不确定性。

      【讨论】:

      • “所以永远不会有多个线程访问同一个对象” 这不能保证。一个线程可以将其线程局部变量的地址传递给另一个线程,该线程可以访问它并导致数据竞争。所以 TLS 让它变得更难,但不能确保不会有来自多个线程的访问。
      • @JonathanWakely,好吧,如果你心情不好,是的。
      • 如果你要纠正 OP 的误解,你最好正确地做到这一点,不要用对地址空间隔离保证或类似的信念代替对同步保证的错误信念。
      • @JonathanWakely,按照这个说法,几乎无法描述任何事情。所有简单的句子通常都变得无效。例如,“使用互斥锁保证不会有超过一个线程进入关键部分”一般来说是错误的。互斥锁可能使用不当,或者代码可能只是绕过互斥锁获取而转到关键部分。为了避免需要在每个陈述前加上冗长的开场白和结语,人们通常会就特定上下文达成一致并假设陈述是在这种上下文中做出的。
      • “根据定义,在堆栈上创建的自动变量是线程安全的,因为始终只有一个线程在使用此堆栈。” - 该声明有点过于强硬/误导;在线程创建者的堆栈上启动线程并且所有指针或对某些自动变量的引用是很常见的——这些衍生线程的访问需要同步,就像全局、静态或实际上是动态分配的变量一样。
      猜你喜欢
      • 2014-06-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-02
      相关资源
      最近更新 更多