将正在使用的线程移动到另一个线程

3
// threadMovedFixed.cpp

#include <iostream> 
#include <thread> 
#include <utility>

int main(){ 

  std::thread t([]{std::cout << std::this_thread::get_id() << std::endl;}); 
  std::thread t2([]{std::cout << std::this_thread::get_id() << std::endl;}); 
  
  t.join();
  t = std::move(t2); 
  t.join(); 
  
  std::cout << "\n";
  std::cout << std::boolalpha << "t2.joinable(): " << t2.joinable() << std::endl;

}

我正在查看以下代码片段,似乎存在几个问题。

(1) 当 t2 在执行其 lambda 表达式时被移动到 t 中时,会发生什么情况?在这种情况下,t2 会立即终止并且 t 重新从头开始执行 t2 的 lambda 表达式,还是会继续执行 t2 停下来的地方?

(2) t 正在接收 t2,但此时 t 可能正在执行其自身的 lambda 表达式。在这种情况下会发生什么?t 是否会立即终止自己的调用以接收 t2?


你是在尝试使用std::move来解决std::thread的问题吗?还是你正在解决由这个片段引起的错误,而这个片段不是你编写的?请提供更多上下文,因为这个片段看起来很奇怪。 - Louis Go
@LouisGo 这是一段关于C++多线程的在线MOOC课程代码片段。 - structuralengin
2个回答

2
在这种情况下,t2会不再引用一个执行线程。
std::thread对象并不是可以终止的东西。它可能引用一个概念上可以终止的执行线程。该执行线程不受移动的影响。移动后,t引用了执行线程。
但t可能正在执行lambda的过程中。
t已经被加入,并且在t2被分配之前不再引用执行线程。
如果t是可连接的,则会调用std::terminate(终止进程)。

所以在这种情况下,如果在 lambda 函数调用中间调用 t = std::move(t2),剩余的工作将被移动到 t,因此 t 将从 t2 离开的地方继续执行? - structuralengin
@structuralengin,它不是“接手其他程序留下的工作”,而只是窃取了处理器,运行在没有意识到你的类的操作系统线程上。 - Dmitry Kuzminov

1
请记住,C++并没有实现所有的多线程功能,而是将其委托给操作系统。C++多线程类只是一个方便的接口包装器,遵循C++惯用语法。
因此,将正在运行的线程移动到另一个std::thread对象中没有问题。否则(从另一个角度看),为什么std:thread甚至有一个移动构造函数?事实上,所有操作系统线程都在运行,即使不知道C++有一个包装器。操作系统线程(例如pthread)继续运行,被另一个对象包装起来。
关于您的第二个问题,t不处于执行中间状态,因为它已经处于不可加入状态。

t2被移动且尚未完成时,可调用单元会发生什么? - structuralengin
@structuralengin,首先,它处于一致的状态:这是移动操作的规则。在另一个对象窃取线程后,线程的唯一一致状态是不可加入的。 - Dmitry Kuzminov
啊,我明白了。所以如果,比如说,t2 在可调用单元被 t 抢走之前只完成了 lambda 调用的一半,那么 t 会完成剩余的可调用单元还是重新开始? - structuralengin
1
C++ 对象都不会“完成”执行,这是 pthread 或 Windows 线程的责任。C++ 只提供您访问它们状态和 API 的方式。因此,在您的情况下,一开始两个 std::thread 都拥有操作系统线程并且可连接。在加入 t 后,它变为不可连接。无论 t2 是否完成,这都没有关系——当您将其移动到 t 时,您使 t 再次可连接,而 t2 不可连接,同时操作系统线程(pthread)在运行时不知道您移动了这些类。 - Dmitry Kuzminov
1
我花了一些时间才理解这个问题,但现在我想我明白了。我可以把std::thread看作是对某些低级pthread的包装器。因此,在这种情况下,如果t2实际上拥有某个pthread,当调用std::move(t2)时,该pthread的所有权将转移到t。所以这里没有发生"线程"的传递。这样说对吗? - structuralengin
你是正确的。请记住,移动系统对象非常简单,因为几乎所有需要移动的内容都是句柄。或者有一个实现对象,你只需要交换两个指针即可。 - Dmitry Kuzminov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接