【问题标题】:Is STL empty() threadsafe?STL empty() 线程安全吗?
【发布时间】:2015-10-23 03:02:18
【问题描述】:

我有多个线程正在修改一个 stl 向量和一个 stl 列表。
如果容器是空的,我想避免锁定

下面的代码是线程安全的吗?如果 items 是列表或地图怎么办?

class A  
{  
    vector<int> items  
    void DoStuff()  
    {  
        if(!items.empty())  
        {  
            AquireLock();  
            DoStuffWithItems();  
            ReleaseLock();  
        }  
     }  
}  

【问题讨论】:

  • 感谢您的回复。为了澄清这个问题:另一个线程将添加到项目中。没有其他线程会从项目中移除——移除只会发生在 DoStuffWithItems() 内部,并且只有一个线程调用 DoStuff()。如果 items.empty() 在另一个线程添加到它时返回 false,它可以。如果 items.empty() 导致应用程序在另一个线程添加到它时崩溃,这是不行的

标签: stl thread-safety


【解决方案1】:

这取决于您的期望。其他答案是正确的,一般,标准 C++ 容器不是线程安全的,此外,特别是您的代码不会阻止另一个修改容器的线程在你调用empty和获取锁之间(但这件事与vector::empty的线程安全无关)。

所以,为了避免任何误解:您的代码不能保证 items 在块内是非空的。

但您的代码仍然有用,因为您要做的就是避免创建多余的锁。您的代码不提供保证,但它可能会阻止不必要的锁创建。它不会在所有情况下都有效(其他线程仍然可以在您的检查和锁之间清空容器),但在 某些 情况下。如果您所追求的只是通过省略冗余锁来进行优化,那么您的代码就可以实现这一目标。

只要确保对容器的任何实际访问都受到锁的保护。

顺便说一句,以上是严格地说未定义的行为:理论上允许 STL 实现在对 empty 的调用中修改 mutable 成员。这意味着对empty 的看似无害(因为只读)的调用实际上会导致冲突。不幸的是,您不能依赖于只读调用对 STL 容器是安全的假设。

不过,在实践中,我很确定vector::empty 不会 修改任何 成员。但是对于list::empty,我已经不太确定了。如果您真的想要保证,那么要么锁定每个访问权限,要么不使用 STL 容器。

【讨论】:

    【解决方案2】:

    STL 的容器和算法中的任何内容都没有线程安全保证。

    所以,不。

    【讨论】:

      【解决方案3】:

      无论空是否是线程安全的,您的代码都不会按照编写的方式实现您的目标。

      class A  
      {  
          vector<int> items  
          void DoStuff()  
          {  
              if(!items.empty())  
              {  
                  //Another thread deletes items here.
                  AquireLock();  
                  DoStuffWithItems();  
                  ReleaseLock();  
              }  
           }  
      }  
      

      更好的解决方案是在每次使用items 时锁定(在迭代、获取项目、添加项目、检查计数/空置等时),从而提供您自己的线程安全。所以,先获取锁,然后检查向量是否为空。

      【讨论】:

      • 你的观点(//另一个线程删除了这里的项目。)很清楚。但同样明显的是,您可以在获得锁后检查 empty() 以确保。
      • @skwllsp:有效点。如果empty() 是线程安全的,则先检查空,锁定,然后重新检查可能会避免不必要的锁定。
      【解决方案4】:

      正如已经回答的那样,上面的代码不是线程安全的,并且在对容器进行实际操作之前必须进行锁定。 但是以下应该比总是锁定具有更好的性能,我想不出它可能不安全的原因。 这里的想法是锁定可能很昂贵,我们会在不需要时避免它。

      class A
      {
          vector<int> items;  
          void DoStuff()  
          {  
              if(!items.empty())  
              {  
                  AquireLock();
                  if(!items.empty())
                  {
                      DoStuffWithItems();  
                  }
                  ReleaseLock();  
              }
           }
       }  
      

      【讨论】:

        【解决方案5】:

        STL 不是线程安全的并且也是空的。如果你想让容器安全,你必须通过互斥锁或其他同步关闭它的所有方法

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-11-19
          • 2011-08-20
          • 1970-01-01
          • 1970-01-01
          • 2012-10-07
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多