【问题标题】:Iterate over STL container using indices safe way to avoid using locks?使用索引安全的方式迭代 STL 容器以避免使用锁?
【发布时间】:2013-03-12 19:42:48
【问题描述】:

想知道以下列方式迭代向量等 STL 容器是否安全,以避免锁定读/写,但只允许任何“写入”线程执行 push_back() 操作。

for (size_t i = 0; i < vec.size(); i++)
{
   const T& t = *vec[i];
   // do something with t
}

我知道迭代器可能会因容器的更改而失效,但也许如果我们确保初始容器大小足够大以供将来添加,那么在不锁定读取或写入的情况下迭代元素也应该是安全的?

【问题讨论】:

    标签: c++ performance stl locking


    【解决方案1】:

    想知道以下列方式迭代向量等 STL 容器是否安全,以避免锁定读/写,但只允许任何“写入”线程执行 push_back() 操作。

    不要,这不是线程安全的。考虑一个试图读取一个值并进入当前缓冲区的线程。此时第二个线程正在增长缓冲区,并且在第一个线程获得指向缓冲区的指针之后,但在它实际读取值之前,第二个线程释放缓冲区的内容。第一个线程正在读取死对象,这是未定义的行为。

    将问题限制为reserve()-ed 向量(即避免增加缓冲区的问题)该方法仍然不是线程安全的。 push_back() 中的代码将执行类似于:

    template <...>
    class vector {
       T *begin,*end,*capacity;   // common implementation uses 3 pointers
    
       void push_back(T value) {
           if (end == capacity) { /* grow vector and insert */ }
           else {
              new (end) T(value);
              ++end;
           }
       }
    };
    

    这里的问题是,如果没有同步机制,编译器可以重新排序指令,递增end 并将其存储到内存中,然后在缓冲区元素上调用T 的构造函数。如果发生这种重新排序,那么您的阅读器线程可能会看到一个值 size(),其中包括当前存储的值,但该值尚未在向量中。

    【讨论】:

    • 我认为问题的最后一句话试图说容量将变得足够大,以至于永远不会导致它改变(尽管显然没有用正确的术语说)。
    • @GManNickG 正是。如果容量足够并且不应该增长,我看不出这个答案有什么资格?如果担心特定实现可能修改缓冲区,那么也许我应该只使用数组?
    • @stgtscc:当向量缓冲区不需要增长时,添加了另一个潜在问题。
    • @DavidRodríguez-dribeas 我喜欢你的解释,这似乎是有道理的。谢谢。
    【解决方案2】:

    据我所知,您不应该编写代码来依赖它作为实现细节,并且不应该是向量导出 API 的一部分。如果它记录的行为,你可以依赖它,如果不是,那么不要这样做。任何依赖于实现且未记录在 API 中的部分都可能在不同平台和同一平台上的不同工具版本上发生变化。

    另外,来自@GManNickG 的评论 --> 您将在调用 size() 时遇到 竞态条件,因为它将在此处被修改和读取而无需锁定。

    【讨论】:

    • 您在size() 上仍然存在竞争条件。
    • 竞态条件对 size() 有哪些可能的副作用?如果只有一个作家呢?
    • 如果只是可以返回“陈旧”大小,那么对于我的特定用例来说这不是一个大问题
    • @stgtscc:这是未定义的行为。只需使用锁或找到并发向量实现并使用它。当现有的解决方案随时可用时,知道某些东西不能保证有效,然后尝试证明使用它的合理性,这是浪费时间。
    • @stgtscc:最糟糕的竞争条件不在size() 函数上,而是在缓冲区上。看我的回答:一个线程可能会增长并释放旧缓冲区,而另一个线程正在尝试从那里读取。
    【解决方案3】:

    你不能依赖关于迭代器不会失效的建议(因为还有很多空间)。为此,您需要 shared_mutex 和 shared_lock 。 (example of usage)

    【讨论】:

      猜你喜欢
      • 2011-08-05
      • 2013-12-08
      • 1970-01-01
      • 2012-04-06
      • 1970-01-01
      • 2011-06-29
      • 1970-01-01
      • 2017-06-21
      • 2013-02-25
      相关资源
      最近更新 更多