简体   繁体   English

停止来自主线程的无限循环线程

[英]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. 我对线程还比较陌生,但我仍在学习最佳技术和C ++ 11线程库。 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. 我对此并不完全确定,因此我开始进行研究,结果发现,实际上同步并不是必需使用volatile,实际上它通常是有害的。 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. 但是,在答案的评论中,此解决方案被描述为无效的,因为volatile不能保证不同的处理器内核能够轻松地(如果有的话)传达volatile值的变化。

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 ? 您是否正在寻找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 ? 我认为您想使用std :: mutex使用屏障同步

Also take a look at boost thread , for a relatively high level threading library 还可以看一下boost线程 ,以获得相对高级的线程库

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. 我建议使用std::mutexstd::condition_variable解决问题。 Here's an example how it can work with C++11: 这是一个示例如何与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. 通过本质上实现自旋锁,您可以对std::atomic进行相同的操作。 Spin locks can be slower than mutexes. 自旋锁的速度可能比互斥锁慢。 So I repeat the advise on the boost website: 因此,我在boost网站上重复了建议:

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. 我相信互斥量和条件变量是解决您的问题的方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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