mardi 3 mars 2015

Understanding why race condition happens when only one thread does the write operation

I recently asked "Thrown object cannot be caught in a multi-threaded solution" and got the correct answer which works perfectly. However, I am still confused why there can be a race condition when only one thread does the write operation. Let me paste the original problematic code:



#include <iostream>
#include <thread>
using namespace std;

struct solution_using_thread {
solution_using_thread()
: alive_(true), thread_() {
thread_ = thread([this]() {
while(alive_);
});
}
~solution_using_thread() {
alive_ = false;
thread_.join();
}
private:
bool alive_;
thread thread_;
};

int main() {
cout << 0 << endl;
try {
solution_using_thread solution;
throw 1;
} catch (int i ) {
cout << i << endl;
}
cout << 2 << endl;
}


Sometimes the output is only



0


According to the linked question, if I instead use member atomic<bool> alive_, the output becomes as expected



0
1
2


Now, I am trying to reason why member bool alive_ causes Undefined Behavior.


Case 1 (Happy ending):



  • Variable solution is initialized:

    • solution_using_thread's default constructor sets alive_ to true in the main thread.

    • The thread starts and the value of alive_ happens to be true in the second thread. So thread execution is stuck in the while loop.

    • Before constructor returns, the second thread has already been started.



  • We throw 1.

    • The destructor of solution is called. The value of alive_ is true in the main thread.

    • thread.join() blocks until the value of alive_ is synchronized with the second thread.

    • After some finite amount of delay alive_ is synchronized, the while loop terminates, the second thread finishes, the thread_.join() returns and stack unwinding is happily completed.



  • The output is 0 1 2


Case 2 (Not desired, but at least not 'Undefined Behavior'):



  • Variable solution is initialized:

    • solution_using_thread's default constructor sets alive_ to true in the main thread.

    • The thread starts and the value of alive_ happens to be false in the second thread. So thread execution ends immediately.

    • Before constructor returns, the second thread have already been started.



  • We throw 1

    • thread.join() returns immediately because the thread has already finished.



  • The output is 0 1 2


Obviously there is at least one more case in which it prints only 0. Can you describe that case?


Aucun commentaire:

Enregistrer un commentaire