问题
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?
回答1:
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.
回答2:
You should not write code to depend on this as its an implementation detail and is not part of the vector's exported API as far as I know. If its documented behavior, you can rely on it, if it's not then don't do it. Anything that is implementation dependent and not a documented part of the API is subject to change on different platforms and on different versions of your tools on the same platform.
Also, from @GManNickG's comment --> You're going to have a race condition on the invocation of size() as it will be modified and read without locking here.
回答3:
You cannot rely on suggestion about iterators will not be invalidated (cus there is many space left). And you need shared_mutex and shared_lock for this. (example of usage)
来源:https://stackoverflow.com/questions/15370644/iterate-over-stl-container-using-indices-safe-way-to-avoid-using-locks