【问题标题】:Looking for clarification of std::join behavior寻找对 std::join 行为的澄清
【发布时间】:2018-02-14 04:14:20
【问题描述】:

我一直在学习更多关于多线程和锁定的知识,这让我想到了http://www.cplusplus.com/reference/thread/thread/join/提供的基本示例

该页面提供的基本解释是:

线程执行完成后函数返回。

好的,听起来好像我们启动了一个线程,当我们启动的线程完成时,我们将恢复调用线程。

这是来自网站的一些示例代码,我已添加了一个打印语句。

#include <iostream>
#include <thread>
#include <mutex>
#include <unistd.h>

std::mutex mtx;

void print_block (int n, char c) {
    mtx.lock();
    for (int i = 0; i < n; i++)
    {
        std::cout << c;
    }
    std::cout << std::endl ;
    mtx.unlock();

    return;
}

int main()
{
    std::thread th1(print_block, 50, '*');
    std::thread th2(print_block, 60, '$');

    th1.join();
    //my print statement
    std::cout << "In between thread joins" << std::endl;
    th2.join();
    
    return 0;
}

根据我看到的 join 描述,我希望输出是:

**************************************************** ***

在线程连接之间

$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ $$$$$$$$$$$$$$

我的逻辑是:

  1. 主线程调用 th1.join,因此主线程暂停执行直到 th1.join 完成。

  2. 打印出星星后th1执行完成,然后我的打印语句执行

  3. 第 2 次执行开始和结束

实际上并非如此,th2 可以在 th1 之前执行,我的 print 语句通常是最后出来的。 join 实际上对主线程做了什么?它似乎并没有像我一直在线阅读那样真正阻止它。

无论如何,我的解释毫无意义,因为它似乎与单线程进程相同。救命!

TLDR;

调用 join 时会发生什么?新线程开始了,但是主线程会发生什么?如果主线程确实阻塞了,为什么我的例子不是这样?

谢谢

编辑:

我想我明白了。出于某种原因,我认为 th1 在调用 join 时开始执行。 th1 在构造函数被调用时开始执行。 Join 将阻塞主线程,直到 th1 开始执行后完成。谢谢大家!

【问题讨论】:

  • join() 发生的事情正是所描述的。但是请注意,您阅读的join() 的描述中没有任何内容可以保证您在连接之前的任何特定执行顺序,当多个执行线程正在运行并且您正在加入其中之一时。事实上,第二个线程可以启动,并设法在第一个线程之前获取互斥锁,从而首先运行。没有任何保证。显示的示例实际上是垃圾。
  • th1 & th2 正在同时运行。当th1 完成并加入时,th2 也已完成,等待您的加入。
  • 我不明白你为什么期望th1.join(); 以某种方式阻止/阻止th2 的执行。您调用th1.join();之前启动了th2。为什么你期望th2 以某种方式“等待”并且只在很久以后才开始?
  • 嗨@AnT,请看我的新编辑。出于某种原因,我认为 join 也开始执行线程。当您调用构造函数时,执行开始。我的错。

标签: c++ multithreading mutex


【解决方案1】:

那个例子不好(原因如下);

调用 join 时会发生什么?新线程开始了,但是 主线程会发生什么?如果主线程确实阻塞,为什么 我的例子不就是这样吗?

th1.join() 将阻塞当前线程(在这种情况下是函数main() 正在运行的执行线程)的执行,直到th1 表示的执行线程完成并且返回。

现在,这与th2 是否在th1 之前完成无关——(不能保证线程th1 进入函数并在线程th2 之前获取锁)。该方法的全部含义是,“th1 完成之前不要继续”。

所以,是的,th2 可能会在th1 之前完成,但是

std::cout << "In between thread joins" << std::endl;

th1 完成之前永远不会执行。


不好的例子:

  • 关于异常安全。首选std::lock_guard。今后请优先使用 cppreference.com
  • int main() 中的 std::cout ... 不受保护,是的,std::cout 不受竞争影响,但在被多个线程使用时不能避免字符输出交错
  • 如果您需要某些函数之间的相对排序,std::thread 可能不是您想要使用的。此外,更喜欢更高的抽象,例如std::async
  • 代码方面,即使int main() 中的std::cout ... 被假定为不可分割的事务,将th1.join() 放在它之前,将th2.join() 放在它之后也只是一个欺骗。我会让连接在代码上相互跟随。

【讨论】:

  • 显示的示例实际上是废话,原因完全不同。它显然假定第一个线程将在第二个线程之前成功锁定互斥锁。显然,这不是真的。
  • 谢谢,我误解了新线程是在你调用join时开始的。在这种情况下,它从您调用构造函数开始。谢谢
猜你喜欢
  • 2023-03-23
  • 2021-08-04
  • 2020-02-10
  • 1970-01-01
  • 2018-10-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多