推送到一个向量并从不同的线程对其进行迭代

Pushing to a vector and iterating on it from different threads

这样一段代码安全吗?

vector<int> v;

void thread_1()
{
    v.push_back(100);
}

void thread_2()
{
    for (int i : v)
    {
        // some actions
    }
}

是否 for (int i : v) 编译成类似的东西:

for ( ; __begin != __end; ++__begin)
{
    int i = *__begin;
}

是否有可能第一个线程中的 push_back 将进行数据重新分配(当大小 == 容量时),删除旧数据,而另一个线程中的 *__begin 将取消引用已释放的迭代器?并且会发生运行时崩溃

如果是这样,我应该如何同步线程?类似于:

void thread_1()
{
    mtx.lock();
    v.push_back(100);
    mtx.unlock();
}

void thread_2()
{
    mtx.lock();
    for (int i : v)
    {
        // some actions
    }
    mtx.unlock();
}

?

简单地说,在一些架构上,Fundamental types本质上是atomic,而在其他架构上则不是。

在这些架构上,只要不发生重新分配并且 int 正确对齐,写入和读取 vector<int> v 是线程安全的;但这取决于各种因素。

但是:

  • 您可能希望避免编写特定于体系结构的代码(除非您希望您的代码基本上 运行 仅在您自己的计算机上运行)

  • 您的代码中没有防止重新分配的机制(这可能会使其他线程持有的迭代器无效),并且由于您也没有同步代码中线程的机制,因此此类重新分配很容易发生。

  • 考虑到你的设计,如果你有 classes/structs 的 std::vector 而不是 Fundamental Types,你也会冒竞争条件 and/or UB 甚至在简单的并发 read/writes 中,因为一个线程可以看到向量的元素处于损坏状态(即线程 2 可以看到向量中的元素#x 它正在被更改{push_back'} 由 thread1)

为了保证线程安全,你有多种选择:

  1. 在 read/written 使用全局互斥机制 read/written 时完全防止对队列进行修改 - 基本上是你在自己的解决方案中所做的 - 使用全局互斥机制
  2. 使用细粒度互斥防止其他线程修改当前正在操作的元素(对于链接数据结构可能会变得棘手)
  3. 使用具有内置机制的线程安全数据结构来确保单个元素不能被多个线程访问
  4. ...