使用 std::atomic 与 std::condition_variable 在 C++ 中暂停和恢复 std::thread 的方法
Approach of using an std::atomic compared to std::condition_variable wrt pausing & resuming an std::thread in C++
这是一个单独的问题,但与我在这里问的上一个问题有关
我在我的C++
代码中使用std::thread
来不断轮询一些数据&将其添加到缓冲区中。我使用C++ lambda
来启动线程,像这样:
StartMyThread() {
thread_running = true;
the_thread = std::thread { [this] {
while(thread_running) {
GetData();
}
}};
}
thread_running
是在类头中声明的atomic<bool>
。下面是我的GetData
函数:
GetData() {
//Some heavy logic
}
接下来我还有一个StopMyThread
函数,我将thread_running
设置为false,以便它退出lambda block
中的while循环。
StopMyThread() {
thread_running = false;
the_thread.join();
}
据我所知,我可以暂停&使用std::condition_variable
恢复线程,如我之前的问题所指出的。
但是如果我只使用std::atomic<bool>
thread_running
来执行或不执行GetData()
中的逻辑,是否有缺点?
GetData() {
if (thread_running == false)
return;
//Some heavy logic
}
与这里所描述的使用std::condition_variable
的方法相比,这会消耗更多的CPU周期吗?
当您想要有条件地停止另一个线程时,条件变量非常有用。所以你可能有一个一直在运行的"工作"线程,当它注意到没有什么事情要做时,它会等待。
原子解决方案需要你的UI交互与工作线程同步,或者非常复杂的逻辑来异步完成。
作为一般规则,你的UI响应线程不应该阻塞工作线程的非就绪状态。
struct worker_thread {
worker_thread( std::function<void()> t, bool play = true ):
task(std::move(t)),
execute(play)
{
thread = std::async( std::launch::async, [this]{
work();
});
}
// move is not safe. If you need this movable,
// use unique_ptr<worker_thread>.
worker_thread(worker_thread&& )=delete;
~worker_thread() {
if (!exit) finalize();
wait();
}
void finalize() {
auto l = lock();
exit = true;
cv.notify_one();
}
void pause() {
auto l = lock();
execute = false;
}
void play() {
auto l = lock();
execute = true;
cv.notify_one();
}
void wait() {
Assert(exit);
if (thread)
thread.get();
}
private:
void work() {
while(true) {
bool done = false;
{
auto l = lock();
cv.wait( l, [&]{
return exit || execute;
});
done = exit; // have lock here
}
if (done) break;
task();
}
}
std::unique_lock<std::mutex> lock() {
return std::unique_lock<std::mutex>(m);
}
std::mutex m;
std::condition_variable cv;
bool exit = false;
bool execute = true;
std::function<void()> task;
std::future<void> thread;
};
或somesuch。
拥有一个线程。只要线程处于play()
模式,线程就会重复运行task。如果在下一次task()
完成时执行pause()
,则工作线程停止。如果在task()
调用结束之前执行play()
,它不会注意到pause()
.
唯一的等待是在销毁worker_thread
时,它会自动通知工作线程它应该退出,并等待它完成。
您也可以手动设置.wait()
或.finalize()
。.finalize()
是异步的,但如果你的应用程序正在关闭,你可以提前调用它,给工作线程更多的时间来清理,而主线程清理其他地方的东西。
.finalize()
不能反转
未测试代码
除非我遗漏了什么,否则您已经在最初的问题中回答了这个问题:您将在每次需要时创建和销毁工作线程。在实际应用程序中,这可能是一个问题,也可能不是。
要解决两个不同的问题,这可能取决于您实际在做什么。一个问题是"我想让线程一直运行,直到我让它停止。"另一种情况似乎是"我有一个生产者/消费者对,并且希望能够在数据准备好时通知消费者"。thread_running
和join
方法适用于第一种方法。其次,您可能希望使用互斥锁和条件,因为您要做的不仅仅是使用状态来触发工作。假设你有一个vector<Work>
。你用互斥锁来保护它,所以条件变成了[&work] (){ return !work.empty(); }
或类似的东西。当wait返回时,您持有互斥锁,这样您就可以从工作中取出事情并执行它们。完成后,返回等待,释放互斥锁,以便生产者可以向队列中添加内容。
您可能想要结合这些技术。拥有一个"完成处理"的原子,所有线程都会定期检查它,以知道何时退出,以便您可以加入它们。使用条件来覆盖线程之间的数据传递情况。
- 使用std::multimap迭代器创建std::list
- C++中std::resize(n)和std::shrink_to_fit之间的区别
- 来自 std::list 的迭代器 .end() 按预期返回"0xcdcdcdcdcdcdcdcd"但 .begin()
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 如何导出包含具有"std::unique_ptr"值的"std::map"属性的
- 从持续时间构造std::chrono::system_clock::time_point
- std::具有相同基类的类的变体
- std::向量与传递值的动态数组
- 使用std::vector的OpenCL矩阵乘法
- 如何在不接触函数本身的情况下暂停和恢复 C++11 std::thread 的功能
- 使用std ::螺纹暂停/恢复异步过程
- 如何在使用管道后将std::cin恢复到键盘上
- 是否可以从std::abort中恢复
- 如何保存和恢复 std::istringstream 的缓冲区?
- c++std::normal_distribution在从文件恢复时给出不一致的随机数
- 使用 std::atomic 与 std::condition_variable 在 C++ 中暂停和恢复 std::thread 的方法
- 如何保存std::mersenne_twister_engine的状态以便以后恢复
- 使用std::acos从std::cos(角度)恢复精确的角度
- 存储和恢复std::vector数据
- 使用std::string键从boost::无序::无序映射中恢复值时出错