5

The scenario is as follows: Thread A keeps executing until it receives a stop signal from thread B that keeps reading input from the console.

What is the best way to implement this? For example, I think I could implement it as a global variable that thread A keeps checking every once in a while, and thread B can change to signal "stop",

But I don't know if this is the correct way.

  • Even if it's correct, should I use "Volatile" or "Atomic<>"? Especially that thread A only reads the value of the variable and thread B only writes to the variable.

  • And what if modifying the variable from thread B right after thread A has read it doesn't matter (doesn't cause problem "thread A quitting time is somewhat relaxed(tolerated after the signal)")?

  • Is there another way for thread B to start thread A and stop it when it wants to?

E_net4
  • 27,810
  • 13
  • 101
  • 139
Oz Le
  • 177
  • 1
  • 9
  • The best way to share data between threads is to encapsulate the thread and data to share in a class. – πάντα ῥεῖ Feb 17 '15 at 17:30
  • 1
    State Not data, although it may not make any difference – Oz Le Feb 17 '15 at 17:33
  • What else means _state_ than having data? Also consider editing your question. That wall of text is hard to read as is. – πάντα ῥεῖ Feb 17 '15 at 17:34
  • 5
    `volatile` has nothing to do with threading, you can forget that idea, it is inherently wrong. Reason is, `volatile` does not provide synchronization, so different threads may "see" different value indefinitely long time, and may access partially modified value. – hyde Feb 17 '15 at 17:43
  • Hyde is inherently wrong. In your use case, thread b setting a volatile bool to true would propagate correctly to thread a which would read the updated value the instant it changed. Volatile means: don't read the cpu cached value, rather reload the value from system memory at every access. On Windows you also gain the fact they are implemented as atom ics guaranteeing exclusive update access. Atomics are the more correct solution but volatile will work. – Sass Feb 17 '15 at 18:00
  • 2
    @KenK according to the standard, there is no guarantee of inter-thread synchronization. In addition, reads and writes may still not be atomic. That sounds more like the definition of `volatile` from Java, which is not the same. – E_net4 Feb 17 '15 at 18:02
  • [Why is volatile not considered useful in multithreaded C or C++ programming?](http://stackoverflow.com/questions/2484980/why-is-volatile-not-considered-useful-in-multithreaded-c-or-c-programming) – Ivan Aksamentov - Drop Feb 17 '15 at 18:23
  • 3
    @KenK: The language gives no guarantee that `volatile` will provide the necessary synchronisation, or that the read will be from "system memory" rather than a CPU cache. Most current mainstream processors have hardware-level cache coherency, which will make your broken assumption work; but that scales badly with the number of processors, and so might well not be the case in future. `atomic` is the correct solution, and `volatile` should never be used for thread interactions. – Mike Seymour Feb 17 '15 at 18:23
  • While volatile isn't normally safe for trying to use something like [Dekker's algorithm](http://en.wikipedia.org/wiki/Dekker%27s_algorithm), because the the ordering of read / writes isn't guaranteed, if only one volatile variable is being changed to indicate a state change, then the order doesn't matter, since the original poster has already stated, that it doesn't matter if "thread a" runs a few extra cycles before stopping. Both multi-core and multi-processor environments include hardware level cache coherency. – rcgldr Feb 17 '15 at 18:48
  • 1
    @rcgldr Not all environments have hardware coherency, and even mainstream hardware might have to abandon that as the number of cores increases. volatile is simply wrong. – Mike Seymour Feb 17 '15 at 19:16
  • 1
    @KenK,@rcgldr `volatile` in C and C++ is meant for accessing things like hardware/IO registers mapped to memory addresses, and similar things. It should not be confused with same keyword of different languages, which may for example provide synchronization guarantees. – hyde Feb 17 '15 at 19:42
  • @hyde - if volatile is guaranteed to work with memory mapped hardware I/O, and if cache coherency is guaranteed in a multi-core or multi-processor environment, then for a single instance state variable, volatile should be sufficient. Dekker's algorithm is an issue only because more than one variable is used for the handshake, and out of order read / write operations can mess up the handshake protocol. Then again, out of order read / write operations with memory mapped hardware I/O would also present issues. – rcgldr Feb 17 '15 at 20:25
  • @rcgldr: Memory mapped hardware is uncached, or has some other guarantee that accesses will reach the hardware. Cache coherency is *not* a universal guarantee; just something that most current hardware provides, to avoid breaking code written with broken assumptions like yours. As I've said a few times now, it becomes less practical as the number of cores increases (since there must be communication channels for each core to invalidate cache lines in every other), so you really can't rely on hardware continuing to support it for much longer. `volatile` is simply wrong; `atomic` is correct. – Mike Seymour Feb 17 '15 at 20:56
  • @rcgldr `volatile` just switches off some optimizations and assumes OS maps IO memory correctly. It does not add synchronization instructions. This might be of interest: http://stackoverflow.com/a/15865359/1717300 – hyde Feb 17 '15 at 21:35
  • @hyde - note in that link, that hardware I/O read / write is done with cache disabled. I don't know what the requirements of "volatile" are. MIcrosoft compilers apparently include synchronization, other compilers may not. The primary issue here would be a "non-coherent" cached write, one that never gets updated in other caches or in physical memory. I'm not aware of any architectures like this, at least not for PC chip sets. Again the main issue with volatile is that it doesn't prevent reordering of reads and writes, but that's not an issue in this case. – rcgldr Feb 17 '15 at 22:13
  • 1
    @rcgldr: Yes, coherence is exactly the issue, as people keep saying. If you only care about architectures you're aware of, then `volatile` might work by accident, but might break in the future as cache coherency becomes increasingly impractical. If you care about portability, then use the language feature that guarantees the necessary synchronisation. That's `atomic`, and not `volatile`. `volatile` has a different purpose, and isn't suitable for thread synchronisation. Don't use `volatile`. Stop arguing that `volatile` might work in some circumstances. Just use the right tool for the job. – Mike Seymour Feb 17 '15 at 22:18
  • @MikeSeymour - I agree that the C++ 11 standard states that std::atomic types should be used instead of volatile for thread synchronization, although Microsoft's compilers default to synchronizing volatile accesses unless a compile time switch to disable this is used. Are there any existing PC based chip sets that do not include cache coherency? What was the reasoning behind the C++ 11 change to the usage of volatile? – rcgldr Feb 17 '15 at 22:42
  • 2
    @rcgldr: C++11 doesn't change the meaning of `volatile`; Microsoft's reinterpretation is not, and never was, standard. C++11 adds atomic types and other primitives to support thread synchronisation, which wasn't supported by earlier standards. As I keep saying, current mainstream PC hardware does support cache coherency; but it's likely that that will change in the future, as the increasing number of cores is likely to make it less practical. If you want to be sure your code will continue to work in the future on non-Microsoft platforms, then you can't abuse `volatile` for this purpose. – Mike Seymour Feb 18 '15 at 02:59
  • @OzLe *"I think I could implement it as a global variable that thread A keeps checking every once in a while"*, for something like this, you would probably want to use [condition variables](http://en.cppreference.com/w/cpp/thread/condition_variable), which will enable the thread to wake up immediately, instead of polling needlessly and both having laggy response and using more CPU than necessary. – hyde Feb 18 '15 at 07:15
  • @OzLe Then on top of condition variables and mutexes, you can implement for example [blocking queues](http://stackoverflow.com/questions/17853875/concurrent-blocking-queue-in-c11) as a slightly higher level abstraction of message passing between threads. – hyde Feb 18 '15 at 07:18
  • @MikeSeymour - "C++ 11 ... meaning volatile ... Microsoft". It wasn't just Microsoft, look at these prior links regarding prior versions of C and volatile: [barrgroup](http://www.barrgroup.com/Embedded-Systems/How-To/C-Volatile-Keyword) , [stack overflow](http://stackoverflow.com/questions/5822386/the-volatile-keyword-in-c-language) (note the 12 up vote answer) , [geeksforgeeks](http://www.geeksforgeeks.org/understanding-volatile-qualifier-in-c) . – rcgldr Feb 18 '15 at 08:13
  • @rcgldr It's irrelevant whether or not it was just Microsoft who added non-standard semantics to "volatile". It's not something you can portably rely on. Please stop this pointless argument for using the wrong thing. "atomic" does the right thing on any standard implementation; "volatile" doesn't. – Mike Seymour Feb 18 '15 at 09:12
  • To support Mike Symour's point here, some multi-threaded PC / Windows games made in the late 1990's and early 2000's will fail intermittently if run on the later released multi-core processors. The work around is to force these games to run on a single core (called "affinity" setting in the case of Windows). – rcgldr Feb 18 '15 at 15:10

5 Answers5

6

The question will probably be closed as "too broad", however I will try to answer it (in the same "broad" fashion).

The only imaginable answer here is: "it depends" (c)

General advise: Try, and keep it simple (KISS principle). Start with a simple solution (like mutexes in your case), and if you ever find it unsatisfactory, replace it with another, more complex but efficient/scalable/customizable/pleasant thing (let's say atomics). If any of this will proof to be insufficient, grow complexity further (relax atomics, add lock-free stuff, build up task-based concurrency, whatever you may need) until you find the right balance.

hichris123
  • 10,145
  • 15
  • 56
  • 70
Ivan Aksamentov - Drop
  • 12,860
  • 3
  • 34
  • 61
3

You don't really want to stop a thread, rather you want to signal it to stop when it is in a good spot.

It doesn't have to be a global variable. You can pass the address of a struct to a thread. That struct could contain a flag. If another thread knew the address of the struct, then it could change the flag. You could have a global array of structs, one for each thread you have running. There are lots of ways for more than one thread to work with the same data.

You could use signals. These are handy if the thread you want to close is sleeping.

Some people like to use mutexes and condition variables. Others like to use atomic operations. If you are just testing a global int to see if it is non zero, a mutex is typically not necessary.

In this case a global int would work fine. You could make it volatile if you wanted. This tells the compiler to not optimize a variable in ways that would prevent your from seeing it get changed externally. Most compilers assume a global variable changes externally and don't need volatile for a global. I will probably get a lot of flack for not being specific about volatile.

You want to ensure the global is zeroed out before your threads run.

Exactly, to the nanosecond, when thread A will see the change is a different issue. If the thread is running concurrently with thread B then it will see it as soon as the change is made. If thread A is asleep it wont see anything. It is even possible thread B can change the flag before thread A starts. When a thread is actually destroyed happens at some indeterminate time after the thread function returns - whenever the OS gets around to it.

johnnycrash
  • 5,184
  • 5
  • 34
  • 58
1

volatile would probably work, but it is not the correct solution. The best you can do is to just check for a std::atomic_bool.

cooky451
  • 3,460
  • 1
  • 21
  • 39
0

You may use a variable to signal "stop" like you've explained, but only under condition that you use memory fences.

See Understanding c++11 memory fences for more details.

Community
  • 1
  • 1
ciamej
  • 6,918
  • 2
  • 29
  • 39
  • 2
    Memory fences are worse in virtually every aspect comparing to locks and atomics in virtually any imaginable application ([more info](http://channel9.msdn.com/Shows/Going+Deep/Cpp-and-Beyond-2012-Herb-Sutter-atomic-Weapons-1-of-2)) – Ivan Aksamentov - Drop Feb 17 '15 at 17:56
0

Instead of having a variable shared between the threads you can use a std::promise and a std::future in order to clearly indicate how the information flows. In the simplest version, you would do something like this:

void threadFunc(std::future<void> shallQuit) {
    using namespace std::chrono_literals;
    while (shallQuit.wait_for(0ms) == std::future_status::timeout) {
      // process data.
    }
}

void otherFunc() {
    std::promise<void> signalQuit;
    auto myThread = std::thread(threadFunc, signalQuit.get_future());
    // ... do other stuff.
    signalQuit.set_value();
    myThread.join();
}

As a generalization, you could e.g. specialize the promise<> for a different type than void and send information about why you want the thread to quit.

tobi_s
  • 1,324
  • 13
  • 16