迭代在STL容器使用索引安全的方式,以避免使用锁? [英] Iterate over STL container using indices safe way to avoid using locks?

查看:165
本文介绍了迭代在STL容器使用索引安全的方式,以避免使用锁?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

想知道如何以下列方式迭代STL容器(例如向量)是安全的,以避免锁定读/写操作,但只允许通过任何写入线程进行push_back()操作。

  for(size_t i = 0; i  {
const T& t = * vec [i];
//使用t
执行某些操作}



我理解迭代器可以由于容器的更改而失效,但也许如果我们确保初始容器的大小足够用于任何未来的添加,它应该也是安全的迭代元素没有锁定读或写?


不是,这不是线程安全的。考虑一个线程试图读取一个值并转到当前缓冲区。此时第二个线程正在生长缓冲区,并且在第一个线程获得指向缓冲区的指针之后,但在它实际读取值之前,第二个线程释放缓冲区的内容。



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

 模板< ...> 
类向量{
T * begin,* end,* capacity; //通用实现使用3个指针

void push_back(T value){
if(end == capacity){/ * grow vector and insert * /}
else {
new(end)T(value);
++ end;
}
}
};

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


Wondering if it's safe to iterate over a STL container such as a vector in the following manner to avoid locking on reads/writes but allowing for push_back() operations only by any "writing" threads.

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

I understand that iterators can be invalidated by changes to the container but perhaps if we make sure the initial container size is large enough for any future additions, it should also be safe to iterate over the elements without locking reads or writes?

解决方案

Wondering if it's safe to iterate over a STL container such as a vector in the following manner to avoid locking on reads/writes but allowing for push_back() operations only by any "writing" threads.

Don't, this is not thread safe. Consider a thread that is trying to read a value and goes to the current buffer. At this time a second thread is growing the buffer, and after the first one obtained the pointer to the buffer, but before it actually read the value, the second thread releases the contents of the buffer. The first thread is reading dead objects, which is undefined behavior.

Restricting the problem to a reserve()-ed vector (i.e. avoiding the problem of growing the buffer) the approach is still not thread safe. The code in push_back() will do something similar to:

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;
       }
   }
};

The problem here is that without synchronization mechanisms the compiler can reorder the instructions, increment end and store that to memory, then call the constructor of T over the buffer element. If that reordering happens, then your reader thread might see a value of size() that includes the value that is currently being stored, but the value is not yet in the vector.

这篇关于迭代在STL容器使用索引安全的方式,以避免使用锁?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆