<p>You have a <a href="https://en.cppreference.com/w/cpp/language/memory_model#Threads_and_data_races" rel="nofollow noreferrer">data race</a> on shutdown.</p>

<blockquote>
  <p>When an evaluation of an expression writes to a memory location and another evaluation reads or modifies the same memory location, the expressions are said to conflict. A program that has two conflicting evaluations has a data race [...]</p>
</blockquote>

<p>In <code>shut()</code> you set the <code>shutdown</code> flag using a mutex, but the check is performed <em>without</em> the mutex (and the <code>State</code> destructor doesn't use a mutex either). Thus you have conflicting operations (read + write) on a non-atomic variable, without the proper happens before relation. This is a data race which results in undefined behavior.</p>

<p>The simple solution would be to make <code>shutdown</code> an <code>std::atomic&lt;bool&gt;</code>, then you wouldn't even need the mutex to set the flag.</p>

<p>For more details about data races and the C++ memory model I can recommend this paper which I have co-authored: <a href="https://arxiv.org/abs/1803.04432" rel="nofollow noreferrer">Memory Models for C/C++ Programmers</a></p>
C++ Thread does not detect global variable change
<p>I have 2 threads monitoring the same global <code>state</code>, if the <code>state.shutdown</code> becomes <code>false</code>, the thread <code>run()</code> should return.  The code is below.</p>

<pre><code>#include &lt;iostream&gt;
#include &lt;chrono&gt;
#include &lt;thread&gt;
#include &lt;mutex&gt;

using namespace std;

struct State {
    bool shutdown = false;

    ~State() {
        shutdown = true;
    }
};

State state;

#define CHECK_SHUTDOWN     \
  {                        \
    std::cout &lt;&lt; (state.shutdown ? " SHUTDOWN " : " NOSHUT ") &lt;&lt; typeid(*this).name() &lt;&lt; std::endl; \
    if (state.shutdown) {  \
        return;            \
    }                      \
  }

class Mythread {
public:
    void join();
    void run();
    void launch();
    std::thread self_thread;
};

void Mythread::run() {
    while(1) {
        CHECK_SHUTDOWN
    }
}

void Mythread::join() {
    if (self_thread.joinable()) {
        self_thread.join();
    }
}

void Mythread::launch() {
    self_thread = std::thread(&amp;Mythread::run, this);
}

std::mutex mtx;
void shut() {
    std::lock_guard&lt;std::mutex&gt; lock(mtx);   
    state.shutdown = true;
}

int main()
{
    Mythread thread1;
    Mythread thread2;

    thread1.launch();
    thread2.launch();

    std::this_thread::sleep_for(std::chrono::milliseconds(1000));


    //state.shutdown = true;
    shut(); //This makes no difference with the line above

    std::this_thread::sleep_for(std::chrono::milliseconds(100));

    thread1.join();
    thread2.join();


    return 0;
}

</code></pre>

<p>However, even I manually set the <code>state.shutdown</code> to be true, the threads can never detect it. I got prints like:</p>

<pre><code> NOSHUT 8Mythread                                                                                                
 NOSHUT 8Mythread                                                                                                
 NOSHUT 8Mythread                                                                                                                                                                                                                

...Program finished with exit code 0                                                                             
Press ENTER to exit console. 
</code></pre>

<p>at the end. I'm also confused given that the <code>run()</code> function is never returned, the threads join should hang. However the threads can join successfully. </p>

<p>Any help would be very appreciated here!</p>