联接线程如何影响主线程中的执行顺序?

How does joining threads affect the order of execution in the main thread?

本文关键字:线程 顺序 执行 影响 接线 何影响      更新时间:2023-10-16

我知道线程并发运行,因此您无法预测执行顺序,但在提供的代码中,我在运行其他任何内容之前加入了线程t4。 如果.join()应该等到线程完成执行,那么为什么顺序仍然是随机的? 在两个 print 语句之前连接任何内容总是会导致它们最后打印,而如果我之后加入所有内容, 它并不总是持久的,为什么?

void task() {
std::cout << "task 1 says Hin";
}
void task2() {
std::cout << "task 2 says Hin";
}
void task3() {
std::cout << "task 3 says Hin";
}
void task4() {
std::cout << "task 4 says Hin";
}
int main() {
std::thread t1(task);
std::thread t2(task2);
std::thread t3(task3);
std::thread t4(task4);
t4.join();
std::cout << "main says Hi 1n";
// synchronize - IMPORTANT!
t2.join();
t3.join();
t1.join();
std::cout << "main says Hi 2" << std::endl;
system("pause");
}

>std::thread::join只阻塞当前线程,直到*this标识的线程完成其执行。

因此,这向您保证的是,main says Hi 1不会在task 4 says Hi之前打印,并且在其他三个任务完成执行之前不会打印main says Hi 2

四个任务中的printf语句可以按任何顺序打印,并且来自不同线程的输出也可能交错打印。

当您在创建线程时启动线程时,线程很可能会在您加入它们之前执行其代码。您首先创建t1t1很可能首先执行,然后在您创建它们的另一个中创建的所有其他执行。

join您的t4不能保证它在t1或您创建的任何其他线程之前执行。它只是等待t4的终止。

Visual Studio中的快速测试显示:

task 4 says Hi
task 2 says Hi
task 3 says Hi
task 2 says Hi
main says Hi 1
main says Hi 2

使用以下代码(代码片段):

// ...
std::thread t4(task2);
std::thread t2(task2);
std::thread t3(task3);
std::thread t2(task2);
// ...

连接线程的终止连接线程中join()的结束同步。简而言之,这意味着join()连接线程中发生的所有事情也保证在连接线程中发生的所有事情之后发生。C++标准中有几个地方与这种关系建立了强同步关系(例如,一个线程中的原子存储具有std::memory_order_seq_cst同步的内存顺序 - 如果加载的值是存储在第一个线程中的值,与另一个线程上的原子加载同步)。还有一些较弱的保证,例如对于内存顺序较弱的原子访问。

您可以将结果执行视为执行由这些同步关系产生的有向无环图的拓扑排序的可能结果之一。(如果图形是循环的,那么你就会死锁。

具体来说,如果不存在从其中一个到另一个的同步路径,则不保证操作的相对顺序。您不能向后遍历边缘,但这似乎是您期望事情解决的方式。仅仅因为主线程在线程 1 之前加入线程 4,并不意味着线程 4 上的任何事情都发生在线程 1 上的任何内容之前。要使这成立,您需要在 DAG 中引入额外的边缘或重新排序操作以使其为真(即可证明)。例如,您可以在线程 4 连接后从主线程启动线程 1 – 然后,线程 1 中发生的任何事情都将发生在线程 4 中发生的所有事情之后,因为线程创建与新线程的开头同步,然后建立如下所示的传递依赖项:

  • 线程 4 执行一些输出。
  • 。这发生在线程 4 终止之前(请注意,此推论仅当两个事件位于同一线程上时才有效)。
  • 。这将与主线程上的thread4.join()同步。
  • 。这发生在主线程上创建thread1之前(同样,只是因为它是同一个线程)。
  • 。这将与thread1上发生的所有操作同步。
  • 。包括在线程 1 上执行的任何输出。

TL;DR:线程同步原则上是单行道。它就像刷新缓冲区(实际上,它可能涉及在硬件级别刷新所谓的存储缓冲区):它保证所有以前的写入都已完成,但它不会延迟任何以前的任何形式的写入。如果需要更严格的保证,则必须使用不同的同步方式。