「C++ 多线程」线程归属权及其转移

文章大图来源: pixiv_id=77108212

线程归属权

基本概念

在 C++ 中,线程归属权是指对线程对象生命周期的控制。当一个线程被创建后,需要确保它被正确地管理。

当我们用 std::thread 类创建了一个线程 t,那么 t 对象就拥有对所创建的新的子线程的归属权。在之后我们可以调用 join() 来等待子线程执行完毕;也可以调用 detach() 将现场分离,在后台独立运行,此时 t 对子线程的归属权被转移。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <thread>

void foo(){
std::cout << "Kirisame Marisa" << "\n";
}

int main(){
std::thread t(foo);
t.join(); // 等待线程执行完毕
// t.detach(); // 不等待,放到后台运行,t的线程归属权被转移

return 0;
}

线程归属权的转移

C++11 引入的移动语义在线程归属权转移上有着重要的作用。std::thread 线程对象是可移动的,但不可拷贝。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include <thread>

void foo() {
std::cout << "Kirisame Marisa" << std::endl;
}

int main() {
std::thread t1(foo);
std::thread t2 = std::move(t1); // t1的线程归属权从转移到t2
// t1 变成空线程,不关联任何线程函数;t2 关联线程函数 foo
// t1 是 unjoinable 的
t2.join();

return 0;
}

在上面的代码中,t2 = std::move(t1)t1 所代表的线程的归属权转移到了 t2。然后,t1 不再拥有线程的归属权,而 t2 负责管理线程的执行和生命周期,关联的是函数 foo(),之后我们选择调用 join() 等待线程 t2 执行完毕。

运行结果如下:

接下来再看一个复杂一点的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
#include <thread>

void foo1(){
std::cout << "Kirisame Marisa" << "\n";
}

void foo2(){
std::cout << "Alice Margatroid" << "\n";
}

int main(){
std::thread t1(foo1); // t1 关联线程函数 foo1
std::thread t2(std::move(t1)); // t1 的线程归属权转移给 t2,t1 变成空线程,t2 关联 foo1
t1 = std::thread(foo2); // t1 关联线程函数 foo2

// 此时两个线程都是 joinable 的
std::cout << "t1 is joinable: " << t1.joinable() << "\n";
std::cout << "t2 is joinable: " << t2.joinable() << "\n";

std::thread t3;
t3 = std::move(t2); // t2 的线程归属权转移给 t3,t2 变成空线程,t3 关联 foo1
// t1 = std::move(t3); // ERROR
// t1 拥有一个线程的归属权(关联 foo2)且还未执行结束(还未 join 或 detach)
// 一个 std::thread 同一时间只能拥有一个线程的归属权

// t2 没有关联任何函数,是 unjoinable 的
t1.join();
t3.join();

return 0;
}

在上面的代码中,创建了一个 std::thread 对象 t1,并将函数 foo1 作为线程接口函数。然后通过 std::thread t2(std::move(t1))t1 所拥有的线程归属权转移给了 t2。此时,t1 变成空线程,t2 关联 foo1

之后,通过 t1 = std::thread(foo2) 使得 t1 重新关联了另一个函数 foo2

通过 t3 = std::move(t2)t2 的线程归属权转移给 t3。此时 t2 变成空线程,t3 关联函数 foo1

在注释的代码中的 t1 = std::move(t3); 操作是不可行的。t1 拥有一个线程的归属权(关联着 foo2 对应的线程)且还未执行结束(还未 joindetach)。

一个 std::thread 对象同一时间只能拥有一个线程的归属权。

运行结果如下:

参考

  1. C++ 并发编程实战 第2章 线程管理

「C++ 多线程」线程归属权及其转移
https://marisamagic.github.io/2024/12/21/20241221/
作者
MarisaMagic
发布于
2024年12月21日
许可协议