简体   繁体   中英

How to end a thread properly?

My main program creates a thread. This thread initializes some data then enters a 'while' loop and runs until the main program sets the control variable to 'false'. Then it calls join() witch blocks the whole code endlessly.

bool m_ThreadMayRun;

void main(){
   thread mythread = thread(&ThreadFunction);
   //do stuff
   m_ThreadMayRun = false;
   mythread.join(); // this blocks endlessly even when I ask 'joinable' before
}

void ThreadFunction{
   initdata();
   m_ThreadMayRun=true;
   while(m_ThreadMayRun){
   //do stuff that can be / has to be done for ever
   }
   deinitdata();
}
  • Am I missing something here?
  • What would be a proper solution to make the loop leave from the main thread?
  • Is it at all necessary to call join?

Thanks for help

You have a race condition for two threads writing to m_ThreadMayRun . Consider what happens if first the main thread executes m_ThreadMayRun = false;and then the thread you spwaned executes m_ThreadMayRun = true; , then you have an infinite loop. However, strictly speaking that line of reasoning is irrelevant, because when you have a race condition your code has undefined behavior.

Am I missing something here?

You need to synchronize access to m_ThreadMayRun by making it either an std::atomic<bool> or using a std::mutex and make sure that m_ThreadMayRun = false is executed after m_ThreadMayRun = true; .

PS For this situation it is better to use a std::condition_variable .

The issue is that access to bool m_ThreadMayRun; is not synchronized, and according to C++ rules, each thread may assume it does not change between threads. So you end up with a race (a form of undefined behavior).

To make the intention clear, make it atomic .

std::atomic<bool> m_ThreadMayRun;

With this every load/store of m_ThreadMayRun becomes a memory fence, which not only synchronizes its own value, but also makes other work done by the thread visible, due to the acquire/release semantics of an atomic load/store.

Though there is still a small race possible between m_ThreadMayRun = true in the thread and setting m_ThreadMayRun = false . Either one can execute first, sometimes leading to undesired results. To avoid this, initialize it to true before starting the thread.

std::atomic<bool> m_ThreadMayRun;

void main(){
   m_ThreadMayRun = true;
   thread mythread(&ThreadFunction);
   //do stuff
   m_ThreadMayRun = false;
   mythread.join(); // this blocks endlessly even when I ask 'joinable' before
}

void ThreadFunction{
   initdata();
   while(m_ThreadMayRun){
   //do stuff that can be / has to be done for ever
   }
   deinitdata();
}

For more details about memory fences and acquire/release semantics, refer to the following excellent resources: the book " C++ Concurrency in Action " and Herb Sutter's atomic<> weapons talk.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM