在多线程代码中是否必须具有原子标记?

问题描述 投票:0回答:2

我想知道在多线程代码中是否真的需要原子标记。对于这个问题,我着眼于多线程代码中的一种常见情况:通过设置标志来停止线程。

假设以下伪代码:

is_running = 1;
create_threads(stopper_thread, running_thread_A, running_thread_B, running_thread_C);


stopper_thread         running_thread_A        running_thread_B        running_thread_C
-------------------------------------------------------------------------------------------
 if (x)             |  while(is_running) {   | while(is_running) {   | while(is_running) {
    is_running = 0; |  }                     | }                     | }

在此伪代码中,所有running_thread_x线程都使用公共变量is_running来检查它们是否正在运行。当我们想在stopper_thread中停止它们时,只需将is_running设置为0。这意味着is_running是线程之间的共享资源。在许多编码示例中,人们都使用std::atomic_flag标志使用原子变量(例如C ++中的is_running)或在关键部分访问此变量,以互斥访问此变量。

但是是否需要同步此标志?

我以某种方式相信,在与上述示例类似的情况下,其中只有stopping operation作为一个或多个stopper线程,实际上没有必要同步对该标志的访问。

为什么?

据我所知,即使我们要停止线程时也可以同时访问多个线程中的is_running标志,此访问不会阻止通过以下方式将该标志从1设置为0:止动螺纹。发生的情况是此更改可能不会立即反映在正在运行的线程中。但这重要吗?我认为不是,因为如果我们不在当前正在运行的线程迭代中从0读取值is_running,那么您将在再经过几次迭代后最终读取它,并且线程将最终停止。因此设置此标志最终将停止所有正在运行的线程,但停止可能会延迟一点。

您如何看待我的论点?我的论点正确吗?还是我可能缺少论证失败的情况?

multithreading
2个回答
1
投票

[std::mutex / pthread_mutex_tstd::condition_variable / pthread_cond_t用于与线程通信时,该标志不应是原子的,因为仅当互斥锁被锁定时,该标志才必须存储和加载。尝试将std::atomic /atomic_flag/ atomic_bool用于标志来绕过互斥锁会导致死锁。

例如:

+-----+--------------------------------+--------------------------------+
|Step |Thread A                        |Thread B                        |
+-----+--------------------------------+--------------------------------+
|1    |                                |lock the mutex                  |
+-----+--------------------------------+--------------------------------+
|2    |                                |check whether the flag is not   |
|     |                                |set or the queue is empty       |
+-----+--------------------------------+--------------------------------+
|3    |set the atomic flag             |                                |
+-----+--------------------------------+--------------------------------+
|4    |notify condition variable       |<notification is lost>          |
+-----+--------------------------------+--------------------------------+
|5    |                                |and if so wait on the condition |
|     |                                |variable.                       |
+-----+--------------------------------+--------------------------------+

在这种情况下,线程A可以在B完成步骤2之后但在完成步骤5之前执行步骤3和4。在这种情况下,丢失了来自步骤4的条件变量通知,导致线程B在步骤5中等待条件变量永远。


0
投票

发生的情况是此更改可能不会立即反映在正在运行的线程中。

发生的事情是这是未定义的行为。允许编译器使用非同步代码执行几乎所有操作。例如,可以重写

while(is_running) { }

进入

auto running = is_running;
while(running) { }

因此,它将永远循环,无论is_running的未来值如何。当is_running声明为atomic时,不允许进行此重写。

此外,即使编译器不重写此代码,也没有原子性,仍然允许CPU执行它(它可以从缓存而不是内存中读取陈旧的值。

人们使用原子的原因是为了避免UB。如果执行多线程,则在同步线程时必须使用同步原语。没有逃生。

© www.soinside.com 2019 - 2024. All rights reserved.