多线程
# 线程概念
# 什么是线程
- 进程里的一个执行流就叫做线程(thread)。更准确的定义是:线程是
一个进程内部的控制序列
- 一切进程至少都有一个执行线程。
- 线程在进程内部运行,本质是在进程地址空间内运行。
- 在Linux系统中,在CPU眼中,看到的PCB都要比传统的进程更轻量化。
- 透过进程虚拟地址空间,可以看到进程的大部分资源,将进程资源合理分配给每个执行流,就形成了线程执行流。
线程是进程中的执行流 看待地址空间和页表:
- 地址空间是进程中能看到的资源窗口
- 页表决定进程能看到的资源窗口
**注意:**Linux没有线程,只是有进程模拟的
# 线程的优点和缺点
- 优点:
- 与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多
- 线程占用的资源要比进程少很多,能充分利用多处理器的可并行数量
- 在等待慢速I/O操作结束的同时,程序可执行其他的计算任务
- 计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现
- I/O密集型应用,为了提高性能,将I/O操作重叠。线程可以同时等待不同的I/O操作。
- 缺点:健壮性较差
线程是进程的执行分支,线程出异常,就类似进程出异常,进而触发信号机制,终止进程,进程终止,该进程内的所有线程也就随即退出。
- 合理的使用多线程,能提高CPU密集型程序的执行效率
- 合理的使用多线程,能提高IO密集型程序的用户体验
# 进程控制
# POSIX线程库
pthread线程库是应用层的原生线程库:
- 要使用这些函数库,要通过引入头文件
<pthread.h>
- 链接这些线程函数库时,要使用编译器命令的
-lpthread
选项
# 创建线程
pthread_create
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
1
参数:
- thread:获取创建成功的线程ID,该参数是一个输出型参数。
- attr:用于设置创建线程的属性,传入NULL表示使用默认属性。
- start_routine:该参数是一个函数地址,表示线程例程,即线程启动后要执行的函数。
- arg:传给线程例程的参数。
返回值:成功返回0,失败返回错误码
例如:
#include <iostream>
#include <pthread.h>
#include <unistd.h>
void* start_routine(void* args) {
while(true) {
std::cout << "--新线程--" << std::endl;
sleep(1);
}
}
int main() {
pthread_t pth = 1;
int n = pthread_create(&pth, nullptr, start_routine, (void*)"thread one");
while(true) {
std::cout << "--主线程--" << std::endl;
sleep(1);
}
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[root@VM-4-9-opencloudos ~]# ps aux | grep mythread
cen 3727139 0.0 0.1 14040 3368 pts/4 Sl+ 21:31 0:00 ./mythread
root 3727456 0.0 0.1 6456 2104 pts/0 S+ 21:32 0:00 grep --color=auto mythread
[root@VM-4-9-opencloudos ~]# ps -aL
PID LWP TTY TIME CMD
3727139 3727139 pts/4 00:00:00 mythread
3727139 3727140 pts/4 00:00:00 mythread
3727753 3727753 pts/0 00:00:00 ps
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
LWP(Light Weight Process)就是轻量级进程的ID,可以看到显示的两个轻量级进程的PID是相同的,因为它们属于同一个进程。
创建一批线程
class ThreadData {
public:
int number;
pthread_t pid;
char buffername[32];
};
const int NUM = 10;
std::vector<ThreadData*> Threaddatas;
void* start_routine(void* args) {
sleep(1);
ThreadData* thd = static_cast<ThreadData*>(args);
std::cout << "新线程 name: " << thd->buffername << std::endl;
return nullptr;
}
// 批量生成线程:
int main() {
for(int i = 1; i <= NUM; i++) {
ThreadData* thd = new ThreadData();
Threaddatas.push_back(thd);
thd->number = i;
snprintf(thd->buffername, sizeof(thd->buffername), "thread:%d", i);
// 创建进程
pthread_t phd = pthread_create(&thd->pid, nullptr, start_routine, thd);
}
for(auto& thread : Threaddatas) {
cout << thread->buffername << endl;
}
cout << "main thread quit" << endl;
return 0;
}
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
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
# 等待线程
一个线程被创建出来,也是需要被等待的。如果主线程不对新线程进行等待,那么这个新线程的资源也是不会被回收的,产生类似于“僵尸进程”的问题,也就是内存泄漏。
pthread_join
int pthread_join(pthread_t thread, void **retval);
1
参数:
- thread:被等待线程的ID。
- retval:线程退出时的退出码信息。
class ThreadData {
public:
int number;
pthread_t pid;
char buffername[32];
};
const int NUM = 10;
std::vector<ThreadData*> Threaddatas;
void* start_routine(void* args) {
sleep(1);
ThreadData* thd = static_cast<ThreadData*>(args);
std::cout << "新线程 name: " << thd->buffername << std::endl;
pthread_exit((void*)thd->number);
return nullptr;
}
// 批量生成线程:
int main() {
for(int i = 1; i <= NUM; i++) {
ThreadData* thd = new ThreadData();
Threaddatas.push_back(thd);
thd->number = i;
snprintf(thd->buffername, sizeof(thd->buffername), "thread:%d", i);
// 创建进程
pthread_t phd = pthread_create(&thd->pid, nullptr, start_routine, thd);
// sleep(1);
}
for(auto& thread : Threaddatas) {
cout << thread->buffername << endl;
}
for(auto& thread : Threaddatas) {
// 等待进程
void* ret = nullptr;
int n = pthread_join(thread->pid, &ret);
assert(n == 0);
cout << thread->buffername << " | " << (long long)ret << " join success!" << endl;
delete thread;
}
cout << "main thread quit" << endl;
return 0;
}
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
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
结果:
thread:1
thread:2
thread:3
thread:4
thread:5
thread:6
thread:7
thread:8
thread:9
thread:10
新线程 name: thread:1
新线程 name: thread:2
新线程 name: thread:4
新线程 name: thread:8
新线程 name: thread:9
新线程 name: 新线程 name: thread:10
新线程 name: 新线程 name: thread:5
thread:3
新线程 name: thread:6
thread:7
thread:1 | 1 join success!
thread:2 | 2 join success!
thread:3 | 3 join success!
thread:4 | 4 join success!
thread:5 | 5 join success!
thread:6 | 6 join success!
thread:7 | 7 join success!
thread:8 | 8 join success!
thread:9 | 9 join success!
thread:10 | 10 join success!
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
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
# 终止线程
- return 终止
void* start_routine(void* args) {
sleep(1);
ThreadData* thd = static_cast<ThreadData*>(args);
std::cout << "新线程 name: " << thd->buffername << std::endl;
pthread_exit((void*)thd->number);
return (void*)0;
}
1
2
3
4
5
6
7
2
3
4
5
6
7
- pthread_exit 终止
pthread_exit
void pthread_exit(void *retval);
1
void* start_routine(void* args) {
sleep(1);
ThreadData* thd = static_cast<ThreadData*>(args);
std::cout << "新线程 name: " << thd->buffername << std::endl;
pthread_exit((void*)0);
}
1
2
3
4
5
6
2
3
4
5
6
# 线程分离
新创建的线程是默认joinable的,线程退出后,需要对其进行pthread_join操作,否则无法释放资源,从而造成内存泄漏。如果不关心线程的返回值,可以将该线程进行分离,后续当线程退出时就会自动释放线程资源。
pthread_detach
int pthread_detach(pthread_t thread);
1
例如:
string getID(const pthread_t& tid) {
char buffer[32];
snprintf(buffer, sizeof(buffer), "0x%x", tid);
return buffer;
}
void* start_routine(void* args) {
string name = static_cast<const char*>(args);
while(true) {
// pthread_self : 获取ID值
std::cout << "new thread" << name << getID(pthread_self()) << " running..." << std::endl;
sleep(1);
}
return nullptr;
}
int main() {
pthread_t tid;
pthread_create(&tid, nullptr, start_routine, (void*)"thread");
pthread_detach(tid);
cout << "main thread running..." << endl;
int n = pthread_join(tid, nullptr);
cout << n << strerror(n) << endl;
return 0;
}
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
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
上次更新: 2025/05/23, 14:23:42