Reputation: 35485
Is this thread-safe?
int x = 0;
std::thread([&]{ x = 1; }).join();
std::cout << x;
Variable x is accessed from two threads without using atomics or locks. However, the call to join()
forces the accesses to x to be sequential.
Is a memory barrier required here?
Upvotes: 6
Views: 183
Reputation: 52129
Yes, that particular code snippet is thread safe; barriers or locks are not required.
This is the timeline of events with respect to your code:
thread 1
--------
|
int x = 0;
(write 0 to x)
|
std::thread thread 2
(start thread 2) --------> --------
| |
join(); x = 1;
(thread 1 suspended) (write 1 to x)
. |
. thread 2 returns
. |
(thread 1 resumes) <------- x
|
std::cout << x;
(read from x)
|
thread 1 returns
|
x
As you can see, at no point is x
being accessed by more than one thread. In fact, the use of join()
effectively makes all accesses to x
happen in sequential order, as you've surmised. The join()
provides the synchronization in lieu of the synchronization you get from locks.
Basically, what you have is an example of how you can have multithreading with zero concurrency.
Of course, this is true only because of the call to join()
, which happens immediately after you create the thread in the code snippet you provide. If you instead had something like this:
int x = 0;
std::thread t([&]{ x = 1; });
std::cout << x;
t.join(); // Move join() call here
The timeline instead may look like this:
thread 1
--------
|
int x = 0;
(write 0 to x)
|
std::thread thread 2
(start thread 2) --------> --------
| |
std::cout << x; x = 1;
(read from x) (write 1 to x) <-- PROBLEM!
| |
join(); |
(thread 1 suspended) |
. |
. thread 2 returns
. |
(thread 1 resumes) <------- x
|
thread 1 returns
|
x
Changing the order of join()
in this manner will introduce a race.
Upvotes: 11