C++多线程

算了,我们走吧!

我们不能。
为什么不能?
我们在等待戈多。

本文主要针对C++多线程编程的过程及常见问题进行总结,如果需要了解线程相关知识,请参考

C++ 多线程使用流程

线程的创建及运行

创建与等待

在多线程程序中,我们需要在主线程中创建子线程,然后等待子线程执行完毕,C++11引入了线程类,使得我们能够按照面向对象的方式使用线程,一个简单的线程创建代码如下:

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

using namespace std;

void threadFun(){
cout << "I am inside a thread function" << endl;
}

int main(){
thread t1(threadFun); //创建一个线程对象,参数是线程函数
t1.join(); //阻塞主线程,直至t1结束执行,即等待线程执行
return 0;
}

类似地,我们可以创建带有参数的线程函数,方法如下:

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

using namespace std;

void threadFun(int value){
cout << "I am inside a thread function" << endl;
cout << "value is " << value << endl;
}

int main(){
thread t1{threadFun, 100}; //通过初始化列表方式创建一个线程对象,参数是线程函数及传入参数
t1.join(); //阻塞主线程,直至t1结束执行,即等待线程执行
return 0;
}

线程函数同样可以为lambda表达式或实例化的模板函数。

从底层了解线程的创建

我们将第一段进行编译,生成汇编代码,首先是线程函数和主函数

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
## 线程函数
.LC0:
.string "I am the thread function"
threadFun():
push rbp
mov rbp, rsp
mov esi, OFFSET FLAT:.LC0
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
mov esi, OFFSET FLAT:_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
mov rdi, rax
call std::basic_ostream<char, std::char_traits<char> >::operator<<(std::basic_ostream<char, std::char_traits<char> >& (*)(std::basic_ostream<char, std::char_traits<char> >&))
nop
pop rbp
ret

## 主函数,重点关注下面的注释内容
main:
push rbp
mov rbp, rsp
push rbx
sub rsp, 24
lea rax, [rbp-24]
mov esi, OFFSET FLAT:threadFun() #保存线程函数地址
mov rdi, rax
call std::thread::thread<void (&)(), , void>(void (&)()) #创建线程
lea rax, [rbp-24]
mov rdi, rax
call std::thread::join() #
mov ebx, 0
lea rax, [rbp-24]
mov rdi, rax
call std::thread::~thread() [complete object destructor] #销毁子线程即t1线程
mov eax, ebx
jmp .L14
mov rbx, rax
lea rax, [rbp-24]
mov rdi, rax
call std::thread::~thread() [complete object destructor] #销毁主线程
mov rax, rbx
mov rdi, rax
call _Unwind_Resume
.L14:
mov rbx, QWORD PTR [rbp-8]
leave
ret

获得线程ID

和进程一样,每个线程都包含自己的ID,我们可以在线程函数中使用this_thread::get_id()来获得当前线程的ID号。

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

using namespace std;

void threadFun(){
cout << this_thread::get_id() << endl;
cout << "I am inside a thread function" << endl;
}

int main(){
thread t1(threadFun); //创建一个线程对象,参数是线程函数
cout << this_thread::get_id() << endl;
t1.join(); //阻塞主线程,直至t1结束执行,即等待线程执行
return 0;
}

线程常见问题及解决方案

参考文献

0%