繁体   English   中英

使用std :: future是安全停止线程的最佳方法吗?

[英]Is using std::future the best way to safely stop a thread?

我读对如何安全地从线程退出教程。

本质上,它将将来对象传递给将要从线程执行的函数,并在每个while循环中检查该将来是否已具有值(如果已存在,则退出线程)。 看到:

void threadFunction(std::future<void> futureObj)
{
    std::cout << "Thread Start" << std::endl;
    while (futureObj.wait_for(std::chrono::milliseconds(1)) == std::future_status::timeout)
    {
        std::cout << "Doing Some Work" << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));

    }
    std::cout << "Thread End" << std::endl;

}

问题是,为了检查是否已设置futureObj ,它必须等待一段时间(此处为1毫秒)。 因此,我在线程的每次迭代上都损失了1毫秒。 这不应该是更可取的:

void threadFunction(bool *shouldStop)
{
    std::cout << "Thread Start" << std::endl;
    while (!*shouldStop)
    {
        std::cout << "Doing Some Work" << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));

    }
    std::cout << "Thread End" << std::endl;

}

为了检查shouldStop ,不浪费时间。 所以我的线程运行得更快。 他为什么不这样做?

更新:我猜这是一个应该做同样事情的简单类。

class Stoppable
{
private:
    std::atomic<bool> _shouldContinue;

public:
    Stoppable()
    {
        _shouldContinue.store(true);
    }

    virtual void run() = 0;
    void operator()()
    {
        run();
    }
    bool shouldContinue()
    {
        return _shouldContinue.load();
    }
    void stop()
    {
        _shouldContinue.store(false);
    }
};

然后,要使用它,只需将Stoppable子类化并执行以下操作:

void MySubClass::run()
{
    while (shouldContinue())
    {
        //...
    }
}

第二个建议:

void threadFunction(bool *shouldStop)

如果您打算从另一个线程设置*shouldStop ,恐怕是错误的。

在这种情况下,它应该为void threadFunction(std::atomic<bool> *shouldStop)

没有一种“正确的方式”向线程发出停止信号,因为这取决于您是希望线程继续工作还是仅在有工作要做时工作。 但是有错误的方法-通常涉及不确定的行为,因为在一个线程中写入非原子变量,而在另一个线程中读取非原子变量。

如果您希望线程一直运行直到停止(例如,这在图形渲染线程或实时游戏逻辑线程中很常见),那么就没有理由等待检查停止信号。

如果您的线程是仅在有工作要做时才运行的工作线程,则使用std::condition_variablestd::mutex和队列以及信号更为正常。

如果很有趣,这是一个工作队列实现的示例。 请注意,这允许在提交方面乱序完成工作。 有许多可用的策略:

#include <condition_variable>
#include <mutex>
#include <queue>
#include <functional>
#include <thread>
#include <vector>
#include <iostream>
#include <iomanip>


struct worker_control
{
    std::queue<std::function<void()>> work_queue;
    std::mutex m;
    std::condition_variable cv;
    bool stop_signal = false;

    void post_work(std::function<void()> f)
    {
        auto lock = std::unique_lock(m);
        work_queue.push(std::move(f));
        lock.unlock();
        cv.notify_one();
    }

    void request_stop()
    {
        auto lock = std::unique_lock(m);
        stop_signal = true;
        lock.unlock();
        cv.notify_all();
    }
};

std::mutex emit_mutex;
template<class...Args>
void emit(Args&&...args)
{
    auto lock = std::unique_lock(emit_mutex);

    std::cout << std::this_thread::get_id() << " : ";

    ((std::cout << args), ...);

    std::cout << '\n';
}

void run_worker(worker_control& control)
{

    auto should_run = [&]
    {
        return not control.work_queue.empty() or control.stop_signal;
    };

    while (1)
    {
        auto lock = std::unique_lock(control.m);

        control.cv.wait(lock, should_run);

        // at this point we own the lock on control.m

        if (not control.work_queue.empty())
        {
            auto work = std::move(control.work_queue.front());
            control.work_queue.pop();
            lock.unlock(); // allow other workers access to the queue
            work();
        }
        else
        {
            // we can only have got here if there is no work to do and we have been asked to stop
            return;
        }
    }
}



int main()
{
    std::vector<std::thread> worker_threads;

    auto control = worker_control();

    worker_threads.emplace_back([&]{ run_worker(control); });
    worker_threads.emplace_back([&]{ run_worker(control); });
    worker_threads.emplace_back([&]{ run_worker(control); });

    control.post_work([]{ emit("the"); });
    control.post_work([]{ emit("quick"); });
    control.post_work([]{ emit("brown"); });
    control.post_work([]{ emit("fox"); });
    control.post_work([]{ emit("jumps"); });
    control.post_work([]{ emit("over"); });
    control.post_work([]{ emit("the"); });
    control.post_work([]{ emit("lazy"); });
    control.post_work([]{ emit("dog"); });

    control.request_stop();

    for (auto& t : worker_threads)
        if (t.joinable())
            t.join();

}

输出示例:

140244960823040 : the
140244960823040 : fox
140244960823040 : jumps
140244960823040 : over
140244960823040 : the
140244960823040 : lazy
140244960823040 : dog
140244969215744 : quick
140244952430336 : brown

https://coliru.stacked-crooked.com/a/c1612695a3cfc955

暂无
暂无

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

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