简体   繁体   中英

Stop infinite looping thread from main

I am relatively new to threads, and I'm still learning best techniques and the C++11 thread library. Right now I'm in the middle of implementing a worker thread which infinitely loops, performing some work. Ideally, the main thread would want to stop the loop from time to time to sync with the information that the worker thread is producing, and then start it again. My idea initially was this:

// Code run by worker thread
void thread() {
    while(run_) {
        // Do lots of work
    }
}
// Code run by main thread
void start() {
    if ( run_ ) return;
    run_ = true;
    // Start thread
}
void stop() {
    if ( !run_ ) return;
    run_ = false;
    // Join thread
}
// Somewhere else
volatile bool run_ = false;

I was not completely sure about this so I started researching, and I discovered that volatile is actually not required for synchronization and is in fact generally harmful. Also, I discovered this answer , which describes a process nearly identical to the one I though about. In the answer's comments however, this solution is described as broken, as volatile does not guarantee that different processor cores readily (if ever) communicate changes on the volatile values.

My question is this then: Should I use an atomic flag, or something else entirely? What exactly is the property that is lacking in volatile and that is then provided by whatever construct is needed to solve my problem effectively?

Have you looked for the Mutex ? They're made to lock the Threads avoiding conflicts on the shared data. Is it what you're looking for ?

I think you want to use barrier synchronization using std::mutex ?

Also take a look at boost thread , for a relatively high level threading library

Take a look at this code sample from the link:

#include <iostream>
#include <map>
#include <string>
#include <chrono>
#include <thread>
#include <mutex>

std::map<std::string, std::string> g_pages;
std::mutex g_pages_mutex;

void save_page(const std::string &url)
{
    // simulate a long page fetch
    std::this_thread::sleep_for(std::chrono::seconds(2));
    std::string result = "fake content";

    g_pages_mutex.lock();
    g_pages[url] = result;
    g_pages_mutex.unlock();
}

int main() 
{
    std::thread t1(save_page, "http://foo");
    std::thread t2(save_page, "http://bar");
    t1.join();
    t2.join();

    g_pages_mutex.lock(); // not necessary as the threads are joined, but good style
    for (const auto &pair : g_pages) {
        std::cout << pair.first << " => " << pair.second << '\n';
    }
    g_pages_mutex.unlock();
}

I would suggest to use std::mutex and std::condition_variable to solve the problem. Here's an example how it can work with C++11:

#include <condition_variable>
#include <iostream>
#include <mutex>
#include <thread>

using namespace std;

int main()
{
    mutex m;
    condition_variable cv;
    // Tells, if the worker should stop its work
    bool done = false;
    // Zero means, it can be filled by the worker thread.
    // Non-zero means, it can be consumed by the main thread.
    int result = 0;

    // run worker thread
    auto t = thread{ [&]{
        auto bound = 1000;
        for (;;) // ever
        {
            auto sum = 0;
            for ( auto i = 0; i != bound; ++i )
                sum += i;
            ++bound;
            auto lock = unique_lock<mutex>( m );
            // wait until we can safely write the result
            cv.wait( lock, [&]{ return result == 0; });
            // write the result
            result = sum;
            // wake up the consuming thread
            cv.notify_one();
            // exit the loop, if flag is set. This must be
            // done with mutex protection. Hence this is not
            // in the for-condition expression. 
            if ( done )
                break;
        }
    } };

    // the main threads loop
    for ( auto i = 0; i != 20; ++i )
    {
        auto r = 0;
        {
            // lock the mutex
            auto lock = unique_lock<mutex>( m );
            // wait until we can safely read the result
            cv.wait( lock, [&]{ return result != 0; } );
            // read the result
            r = result;
            // set result to zero so the worker can 
            // continue to produce new results. 
            result = 0;
            // wake up the producer
            cv.notify_one();
            // the lock is released here (the end of the scope)
        } 
        // do time consuming io at the side. 
        cout << r << endl;
    }

    // tell the worker to stop
    {
        auto lock = unique_lock<mutex>( m );
        result = 0;
        done = true;
        // again the lock is released here
    }

    // wait for the worker to finish.
    t.join();

    cout << "Finished." << endl;
}

You could do the same with std::atomic s by essentially implementing spin locks. Spin locks can be slower than mutexes. So I repeat the advise on the boost website:

Do not use spinlocks unless you are certain that you understand the consequences.

I believe that mutexes and condition variables are the way to go in your case.

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