StackedCrooked
StackedCrooked

Reputation: 35485

Is it safe to access a variable from the main thread after it was written to by another thread and after that thread was joined?

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

Answers (1)

In silico
In silico

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

Related Questions