2

As the title suggest - in C++, how writes to a shared variable in threads are handled? Do threads work with their own copy of data? I know what is shared between threads and what is not but I could not find anything on how writes to a variable are handled in multithreading scenario.

Let me give you a little background. In Java, threads work on their own copy of data. We add synchronization to ensure that the writes of thread are visible to other threads. In short, synchronization ensures that the writes are flushed to the main memory.

Is similar construct valid for c/c++? If a thread is writing to a variable, are those writes immediately updated to the main memory or is it the OS that governs how the write to main memory will occur? Do we need to add synchronization to flush those updates?

For e.g. If two threads are incrementing a counter, and I can guarantee that the first thread will always finish first with that particular task (somehow by using delayed start of second thread, may not be able to guarantee it in practice) and there won't be any mangling of instructions between the two threads, then would I always see the affect of both the threads?

Thread 1 { i++; ... do something that takes long time}
Thread 2 { i++; ... do something that takes long time}

Thanks, RG

  • 1
    In Java, threads do NOT work on their own copy of data. In Java you synchronize to avoid threads accessing the same data simultaneously, as doing so will lead to a crash. The same applies to C++. – rustyx Dec 03 '20 at 15:22
  • Note that you don't use any synchronization, you may easily end up with _data race_ that in C++ causes _undefined behavior_. This is, for example, the case when `i` is of type `int` and its increment is not protected by a mutex. If `i` is of type `std::atomic`, then, you are fine. – Daniel Langr Dec 03 '20 at 15:35

2 Answers2

2

Do threads work with their own copy of data?

No, not unless the data is declared thread_local.

synchronization ensures that the writes are flushed to the main memory. Is similar construct valid for c/c++? If a thread is writing to a variable, are those writes immediately updated to the main memory

Yes, but unless you use synchronization of some sort, the changes may or may not be seen - or read incorrectly, by other threads.

Do we need to add synchronization to flush those updates?

Yes, but I'm not sure I'd use the word flush. "Synchronization" is good.

For e.g. If two threads are incrementing a counter, and I can guarantee that the first thread will always finish first ...

If you can guarantee that, you have synchronization.

would I always see the affect of both the threads?

Yes, if you have the guarantee you talk about.

Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • Thanks Ted. From [link](https://stackoverflow.com/questions/1850270/memory-effects-of-synchronization-in-java) , so the synchronization ensures what become visible to other threads. There is no guarantee that a write would become visible unless the reader and writer use synchronization. In C++, from what I gather if a thread is updating shared variable, it would be visible to other thread when the other thread looks for it, without explicit synchronization construct. Is that so? – user14757101 Dec 03 '20 at 15:42
  • @user14757101 No. As I wrote in the answer, without synchronization the other threads may or may not see an update or see "half" of it. – Ted Lyngmo Dec 03 '20 at 15:45
  • 1
    Thanks Ted! Yeah, I somehow missed that one. – user14757101 Dec 03 '20 at 15:47
1

C++ has no guarantees about how atomicity of reads/writes and flushes to memory work, unless you use the atomic primitives such as std::atomic. Be very careful here. Lock-free programming is extremely hard to get right.

My advice is use locks.

Tumbleweed53
  • 1,491
  • 7
  • 13
  • Exactly. You want to look into mutexes or semaphores (http://faculty.cs.niu.edu/~hutchins/csci480/semaphor.htm) if you want to make sure that variables are safely used amongst threads – Russell Islam Dec 03 '20 at 15:31
  • 1
    @RussellIslam Why to use a non-standard library? The C++ Standard contains mutexes since C++11. – Daniel Langr Dec 03 '20 at 15:36
  • Thanks Tumbleweed53! So flushing changes to memory is handled by OS unless mutexes, or atomic variables are used to trigger those flushes? – user14757101 Dec 03 '20 at 15:45
  • @user14757101 not just the OS, the CPU and memory bus itself. Using `std::atomic` allows you to (a) prevent the compiler from re-ordering your memory reads and writes, (b) ensure reads and writes are completed atomically and not split into parts, and (c) ensure the processor doesn't reorder your reads and writes. The latter part (c) is important especially for multi-processor systems (basically every system nowadays). However, even understanding and using the `std::atomic` API is a real challenge and it's easy to create bugs. This is just a hard domain. That is why people use locks. – Tumbleweed53 Dec 03 '20 at 16:02