C ++ - atomistic vector is completely thread safe?

I have std::vector<std::atomic<size_t>> vec. Is it safe to run vec[index].fetch_add(1, std::memory_order_release) or store / load with multiple parallel threads? I think this should be because reading is thread safe and writing a single record at the same time from multiple threads is not possible due to the automatics - is this correct?

+5
source share
4 answers

No, no, in general, thread safety, since the container itself is not atomic.

However, until you change what is in the vector (i.e. do something that invalidates the return of data() ), everything will be fine.

Unfortunately, you cannot resort to std::atomic<std::vector<...>> , since std::vector cannot be trivially copied.

+6
source

Your expression vec[n].atomic_op(...) itself not atomic, but decomposes into:

 auto iter = vec.begin(); iter += n; iter->atomic_op(...); 

So, the first two statements are vulnerable to the usual iterator invalidation rules. Simultaneous resizing of the vector or erasing of any element to the nth or nth element itself can violate them.

As soon as you have an iterator for an element, if this iterator has not been canceled during its use, then any atomic operations that you perform on the element itself will be safe.

+3
source

vec[X].fetch_add is safe if you modify every single atomic . If you call any non-constant (modifying) method of wrapping std::vector , this behavior is undefined, since std::vector is not thread safe by itself.

De-facto, you can initialize a vector in some thread, transfer its link to some asynchronous task and from this point act only on certain elements of the vector, and not affect the vector itself. vec[X].action(...) is thread safe, vec.action(...) not.

+3
source

It depends on what other threads are doing.

From the standard:

17.6.5.9 Data Race Evasion [res.on.data.races]

...

2 The standard C ++ library function should not directly or indirectly access objects (1.10) that are accessible by streams other than the current stream if objects are directly or indirectly accessed through function arguments, including this.

3 The standard C ++ library function should not directly or indirectly modify objects (1.10) accessible by flows other than the current stream if objects are accessed directly or indirectly through functions that are not constant arguments, including this.

and

23.2.2 Racing container data [container.requirements.dataraces]

1 In order to avoid data investigations (17.6.5.9), implementations should take into account the following functions: const: start, end, rbegin, rend, front, back, data, find, lower_bound, upper_bound, equal_range, at and, with the exception of associative or disordered associative containers, operator [].

2 Despite (17.6.5.9), implementations are necessary to avoid data investigations when the contents of the contained object in different elements in the same container, except for the <bool> vector, are modified simultaneously.

By combining these two parts with the rules of atomatics, we can conclude that a call to vec[index].fetch_add(1, std::memory_order_release) cannot cause a race condition with other threads that perform the same or different "const" operations (including those specified in paragraph 23.2.2.1). However, if another thread calls a non-const operation on the vec itself (for example, insert , erase , resize , etc.), then we perform undefined behavior, as described in section 1.10:

1.10 Multithreaded Executions and Data Schedules [intro.multithread]

...

4 Two evaluations of expressions conflict if one of them changes the memory location (1.7), and the other accesses or changes the same memory location.

...

21 A program execution contains a data race if it contains two conflicting actions in different threads, at least one of which is not atomic, and the other does not occur. Any such data race results in undefined.

+2
source

Source: https://habr.com/ru/post/1271049/


All Articles