c ++在线程之间共享状态的最佳方式
c++ Best Way to Share state between threads
场景如下:线程A一直执行,直到它收到线程B的停止信号,线程B不断从控制台读取输入。
实现这个的最佳方法是什么?
例如,我认为我可以将它实现为线程 A 每隔一段时间检查一次的全局变量,线程 B 可以更改为信号 "stop"、
但是我不知道这样是不是正确的方法
就算正确了,我应该用"Volatile"还是"Atomic<>"?特别是线程 A 只读取变量的值而线程 B 只写入变量。
而且如果在线程 A 读取后立即从线程 B 修改变量怎么办(不会导致问题 "thread A quitting time is somewhat relaxed(tolerated after the signal)")?
线程 B 是否有另一种方法来启动线程 A 并在需要时停止它?
您可以像您解释的那样使用变量来发出信号 "stop",但前提是您使用内存栅栏。
有关详细信息,请参阅 Understanding c++11 memory fences。
volatile 可能会起作用,但这不是正确的解决方案。你能做的最好的就是检查 std::atomic_bool.
问题可能会作为 "too broad" 结束,但我会尝试回答它(以相同的 "broad" 方式)。
这里唯一可以想到的答案是:
"it depends" (c)
一般建议:
尝试并保持简单 (KISS principle)。
从一个简单的解决方案开始(例如您的情况下的互斥体),如果您发现它不令人满意,请将其替换为另一个更复杂但 efficient/scalable/customizable/pleasant 的东西(比如说原子)。如果其中任何一个证明是不够的,进一步增加复杂性(放松原子性,添加无锁的东西,建立基于任务的并发,无论你需要什么)直到找到合适的平衡。
您并不是真的想停止一个线程,而是想在它处于合适的位置时发出停止信号。
它不一定是全局变量。您可以将结构的地址传递给线程。该结构可以包含一个标志。如果另一个线程知道结构的地址,那么它可以更改标志。您可以拥有一个全局结构数组,每个线程对应一个 运行ning。多个线程处理同一数据的方法有很多种。
您可以使用信号。如果您要关闭的线程正在休眠,这些将很方便。
有些人喜欢使用互斥量和条件变量。其他人喜欢使用原子操作。如果您只是测试一个全局 int 以查看它是否非零,则通常不需要互斥量。
在这种情况下,全局 int 可以正常工作。如果你愿意,你可以让它变得不稳定。这告诉编译器不要以阻止您看到它在外部发生更改的方式优化变量。大多数编译器假定全局变量在外部发生变化,并且不需要 volatile 作为全局变量。我可能会因为没有具体说明 volatile 而受到很多批评。
您要确保在线程 运行 之前将全局清零。
确切地说,到纳秒,线程 A 何时会看到更改是另一个问题。如果线程 运行ning 与线程 B 并发,那么它会在更改发生后立即看到它。如果线程 A 处于睡眠状态,它什么也看不到。线程 B 甚至可以在线程 A 启动之前更改标志。当线程实际上被销毁时,发生在线程函数 returns 之后的某个不确定时间 - 只要 OS 绕过它。
您可以使用 std::promise
和 std::future
而不是在线程之间共享变量,以便清楚地指示信息的流动方式。在最简单的版本中,您可以这样做:
void threadFunc(std::future<void> shallQuit) {
using namespace std::chrono_literals;
while (shallQuit.wait_for(0ms) == std::future_status::timeout) {
// process data.
}
}
void otherFunc() {
std::promise<void> signalQuit;
auto myThread = std::thread(threadFunc, signalQuit.get_future());
// ... do other stuff.
signalQuit.set_value();
myThread.join();
}
作为概括,您可以例如将 promise<>
专门化为不同于 void
的类型,并发送有关您希望线程退出的原因的信息。
场景如下:线程A一直执行,直到它收到线程B的停止信号,线程B不断从控制台读取输入。
实现这个的最佳方法是什么? 例如,我认为我可以将它实现为线程 A 每隔一段时间检查一次的全局变量,线程 B 可以更改为信号 "stop"、
但是我不知道这样是不是正确的方法
就算正确了,我应该用"Volatile"还是"Atomic<>"?特别是线程 A 只读取变量的值而线程 B 只写入变量。
而且如果在线程 A 读取后立即从线程 B 修改变量怎么办(不会导致问题 "thread A quitting time is somewhat relaxed(tolerated after the signal)")?
线程 B 是否有另一种方法来启动线程 A 并在需要时停止它?
您可以像您解释的那样使用变量来发出信号 "stop",但前提是您使用内存栅栏。
有关详细信息,请参阅 Understanding c++11 memory fences。
volatile 可能会起作用,但这不是正确的解决方案。你能做的最好的就是检查 std::atomic_bool.
问题可能会作为 "too broad" 结束,但我会尝试回答它(以相同的 "broad" 方式)。
这里唯一可以想到的答案是: "it depends" (c)
一般建议: 尝试并保持简单 (KISS principle)。 从一个简单的解决方案开始(例如您的情况下的互斥体),如果您发现它不令人满意,请将其替换为另一个更复杂但 efficient/scalable/customizable/pleasant 的东西(比如说原子)。如果其中任何一个证明是不够的,进一步增加复杂性(放松原子性,添加无锁的东西,建立基于任务的并发,无论你需要什么)直到找到合适的平衡。
您并不是真的想停止一个线程,而是想在它处于合适的位置时发出停止信号。
它不一定是全局变量。您可以将结构的地址传递给线程。该结构可以包含一个标志。如果另一个线程知道结构的地址,那么它可以更改标志。您可以拥有一个全局结构数组,每个线程对应一个 运行ning。多个线程处理同一数据的方法有很多种。
您可以使用信号。如果您要关闭的线程正在休眠,这些将很方便。
有些人喜欢使用互斥量和条件变量。其他人喜欢使用原子操作。如果您只是测试一个全局 int 以查看它是否非零,则通常不需要互斥量。
在这种情况下,全局 int 可以正常工作。如果你愿意,你可以让它变得不稳定。这告诉编译器不要以阻止您看到它在外部发生更改的方式优化变量。大多数编译器假定全局变量在外部发生变化,并且不需要 volatile 作为全局变量。我可能会因为没有具体说明 volatile 而受到很多批评。
您要确保在线程 运行 之前将全局清零。
确切地说,到纳秒,线程 A 何时会看到更改是另一个问题。如果线程 运行ning 与线程 B 并发,那么它会在更改发生后立即看到它。如果线程 A 处于睡眠状态,它什么也看不到。线程 B 甚至可以在线程 A 启动之前更改标志。当线程实际上被销毁时,发生在线程函数 returns 之后的某个不确定时间 - 只要 OS 绕过它。
您可以使用 std::promise
和 std::future
而不是在线程之间共享变量,以便清楚地指示信息的流动方式。在最简单的版本中,您可以这样做:
void threadFunc(std::future<void> shallQuit) {
using namespace std::chrono_literals;
while (shallQuit.wait_for(0ms) == std::future_status::timeout) {
// process data.
}
}
void otherFunc() {
std::promise<void> signalQuit;
auto myThread = std::thread(threadFunc, signalQuit.get_future());
// ... do other stuff.
signalQuit.set_value();
myThread.join();
}
作为概括,您可以例如将 promise<>
专门化为不同于 void
的类型,并发送有关您希望线程退出的原因的信息。