从main停止无限循环线程

时间:2013-12-17 17:24:47

标签: c++ multithreading c++11 volatile

我对线程比较新,我还在学习最好的技术和C ++ 11线程库。现在我正在实现一个无限循环,执行一些工作的工作线程。理想情况下,主线程会不时地停止循环以与工作线程正在生成的信息同步,然后再次启动它。我的想法最初是这样的:

// 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;

我对此并不完全确定,所以我开始研究,我发现实际上不需要volatile来实现同步,实际上通常是有害的。另外,我发现了this answer,它描述了一个与我的过程几乎相同的过程。然而,在答案的评论中,这个解决方案被描述为已经破解,因为volatile不能保证不同的处理器核心(如果有的话)能够在易失性值上进行通信的变化。

我的问题是:我应该使用原子旗帜还是完全不同的东西?究竟什么是缺乏volatile的属性,然后由有效解决我的问题所需的任何构造提供?

3 个答案:

答案 0 :(得分:2)

你找过Mutex吗?它们可以锁定线程,避免共享数据冲突。这是你在找什么?

答案 1 :(得分:1)

我认为你想使用barrier synchronization使用std::mutex

另请查看boost thread,了解相对较高级别的线程库

从链接中查看此代码示例:

#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();
}

答案 2 :(得分:0)

我建议使用std::mutexstd::condition_variable来解决问题。下面是一个如何使用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;
}

你可以通过实质上实现自旋锁来对std::atomic做同样的事情。旋转锁可以比互斥锁慢。所以我在boost网站上重复建议:

  

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

我相信互斥体和条件变量是您的理由。