这就像一个线程池上下文。我有一个全局原子变量,它指示工作线程是否应该退出:
atomic<bool> exit;
此类工作线程按如下方式循环执行:
while (true) {
std::unique_lock lock{mut};
cv.wait(lock, [] {
return !queue.empty() || exit.load();
};
if (exit.load()) {
return;
}
// take task from queue
}
还有其他有效的方法(具有更好的性能、更低的延迟)来检查我们是否应该退出工作线程吗?
为了最大限度地降低检查标志的吞吐量成本
exit_now
,请使用.load(relaxed)
。如果同一缓存行中没有任何内容被任何线程频繁写入,那么成本将非常低。也就是说,没有错误共享,每个线程都可以让缓存行处于MESI共享状态,并通过加载它来获得 L1d 缓存命中。(或者至少是 L2 或 L3 命中,如果你在检查之间做了大量工作。)
relaxed
加载不需要任何额外的排序或障碍,因此它们与访问非原子全局变量一样便宜(在无法将其优化为循环持续时间的寄存器的情况下)。相关:如果使用“memory_order_relaxed”检查,为什么使用“memory_order_seq_cst”设置停止标志?
但这只是最大限度地减少了检查
exit
标志的开销。您可以使用无锁队列,在队列为空时阻塞,并
exit
在每项操作后检查标志。我猜,如果您无法在设置退出标志后将某些操作加入队列以确保读取器被唤醒,那么退出延迟与浪费吞吐量(即使队列中没有工作,也只是为了检查标志)之间的权衡就是一种权衡。另一个选项可能是取消其他线程,例如发送 POSIX 信号,并有一个退出的信号处理程序。这可以唤醒线程并使其退出,即使它正处于阻塞状态以等待条件变量(或 C++20
std:wait()
)。在 C++20 中,
std::jthread
] 2是可取消的,而在 C++17 及更早版本中,您必须以不太可移植的方式自行实现。