0

Using std::thread to run a method which runs an infinite loop, is there a way the loop can query if the thread has been requested to join... or do I manually have to add a "exitThread" flag?

In other words what would isJoined look like (untested pseudo code):

std::atomic<int> global_counter (0);

void Run()
{
  while(!isJoined())
  {
    doSomething();
    ++global_counter;
  }
}

int main()
{
  thread t(Run);
  Sleep(10000);
  t.join();
  cout << "Iterated " << global_counter << "times" << endl;
}
Mr. Boy
  • 60,845
  • 93
  • 320
  • 589

2 Answers2

8

What you are trying to do is not only joining the thread, but also sending it a message. These two things are different and you need to do both.

To send the thread a message, you signal it using a conditional variable. Now that you know the name of the thing you need, you can google and you will find tons of good tutorials explaining how to signal threads using this concept, so I feel it is not necessary to go into details here.

Daniel S.
  • 6,458
  • 4
  • 35
  • 78
  • Ah, I have slightly misunderstood what join does... rather than terminating the thread, it just blocks the current/calling thread _until_ the other thread terminates. It makes no attempt to inform the thread it _should_ exit, it just waits for it to do so? That makes it clear an explicit signal (a flag or a conditional variable) is required. – Mr. Boy Nov 04 '15 at 12:31
  • 1
    @Mr.Boy the `join` is a simple `waitpid` call, it doesn't inform the target thread anything, just waiting. check the manpage. – Zang MingJie Nov 04 '15 at 12:37
  • 1
    People always over-design things, because nothing bad will happen, just inefficient. But if you know the detail, simpler and more efficient design also fits – Zang MingJie Nov 04 '15 at 13:07
-2

Use a variable to indicate exit:

volatile boolean request_exit = false;

Test the variable in the loop:

while(!request_exit) { do work }

Then set the variable before join:

request_exit = true;
t.join();
Zang MingJie
  • 5,164
  • 1
  • 14
  • 27
  • 2
    Note that by just using `volatile`, you're not removing any randomness from the program and only ensuring that value will eventually be read by the other thread, whereas it might never be read without the keyword. It guarantees no order of operations whatsoever. – rubenvb Nov 04 '15 at 12:28
  • 1
    A well designed multi-thread program doesn't guarantee the order of exit, it only guarantees the target thread will see it. It is the target thread's duty to exit properly. – Zang MingJie Nov 04 '15 at 12:30
  • So it should use an atomic bool? – Mr. Boy Nov 04 '15 at 12:31
  • @Mr.Boy it is atomic by default – Zang MingJie Nov 04 '15 at 12:34
  • It is not atomic by default. This is a misconception. See http://stackoverflow.com/questions/29633222/c-stdatomicbool-and-volatile-bool. Using `volatile bool` ensures that the read is not omitted, but if you have multiple cores, the value of `request_exit` could still be different in each cores cache. You need a memory barrier for this, hence `std::atomic`. – Excelcius Nov 04 '15 at 12:39
  • @Excelcius you totally misunderstand what's atomic. atomic means two or more r/w operations act like one. Eg, atomic{x=1,y=1}, atomic{x=2,y=2}, there are two atomic ops, the atomic ensures the other cores never see {x=1,y=2} or {x=2,y=1}. Since there is only one write operate, there is nothing to do with atomic. – Zang MingJie Nov 04 '15 at 12:48
  • @Excelcius and the memory barrier ensure one operation occur before another, in this question, we don't care the order of `assign` and `join` from other cores perspective. It's only mater for the current core runs `assign` before `join` – Zang MingJie Nov 04 '15 at 12:51
  • @ZangMingJie Sorry, I might have over-exaggerated. My point is: `volatile` is no longer very useful for multithreading application these days. It could still be read from cache. Therefore I strongly suggest to use `std::atomic` for this and stop using `volatile`, even if it does work in this case. Even if it works for this simple example, it might not work anymore when more functionality is added. Here's another discussion on this topic: http://stackoverflow.com/questions/6995310/is-volatile-bool-for-thread-control-considered-wrong – Excelcius Nov 04 '15 at 12:57
  • 1
    Nothing prevent cpu read from cache, even locks/barriers. They only guarantee the order and the atomic I explained up. Eg, core1: atomic{x=1,y=1}, atomic{x=2,y=2} barrier send_signal. now core2 may read {x=1,y=1} or {x=2,y=2} but after core2 received the signal, core2 can only see {x=2,y=2}. The barrier only ensure assign happens before the signal, it does ensure core2 will see the new value – Zang MingJie Nov 04 '15 at 13:00
  • Locks / barriers won't prevent it. But `std::atomic` does. Locks are also able to deal with this because they cause single-threaded execution. Reading from cache is ok, but for atomic operations, the cache has to be coherent first. This can be ensured by the compiler. There are other issues with `volatile` as well. Just search for volatile vs. atomic, you will find many reasons why one should stop using `volatile` for these applications. Note that this is about `volatile` in C++, which is very different from `volatile` in C# or Java. – Excelcius Nov 04 '15 at 13:11
  • Please read this excellent article about volatile and the confusions which can arise from it: http://www.drdobbs.com/parallel/volatile-vs-volatile/212701484 This will clear a lot about this discussion. – Daniel S. Nov 04 '15 at 14:58
  • Apart from everything else, volatile has ANY relationship to atomic ONLY in MSVC (where it is stated), and possibly implicitly in other X86 compilers due to cache coherence. Might change tomorrow :) do not use volatile in place of atomic. – SergeyA Nov 04 '15 at 15:04
  • I'm not getting the reason for this discussion. I think this answer is completely fine. – Daniel S. Nov 04 '15 at 16:24
  • I don't see any need for any atomics, volatile, or anything other than a flag variable to communicate the thread should terminate. The example has no timing or ordering dependencies or really any inter-thread communication other than the need to flag the thread to exit. There is no reason for global_counter to be atomic. – doug Nov 06 '15 at 21:33