cen's blog cen's blog
首页
  • 编程文章

    • markdown使用
  • 学习笔记

    • 《JavaScript教程》
    • C++学习
    • C++数据结构
    • MySQL
    • Linux
  • 高中时代
  • 工作日常
  • CLion
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • 分类
  • 标签
  • 归档
关于
GitHub (opens new window)

cen

十年饮冰,难凉热血
首页
  • 编程文章

    • markdown使用
  • 学习笔记

    • 《JavaScript教程》
    • C++学习
    • C++数据结构
    • MySQL
    • Linux
  • 高中时代
  • 工作日常
  • CLion
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • 分类
  • 标签
  • 归档
关于
GitHub (opens new window)
  • Linux环境搭建
  • 基本指令
  • 权限
  • Linux基础开发工具
  • 进程概念
  • 进程控制
  • 基础IO流
  • 动态库和静态库
  • 进程通信
  • 进程信号
  • 多线程
    • 线程概念
      • 什么是线程
    • 线程的优点和缺点
    • 进程控制
      • POSIX线程库
      • 创建线程
      • 等待线程
      • 终止线程
      • 线程分离
  • 线程安全
  • Linux
cen
2025-04-21
目录

多线程

# 线程概念

# 什么是线程

  • 进程里的一个执行流就叫做线程(thread)。更准确的定义是:线程是一个进程内部的控制序列
  • 一切进程至少都有一个执行线程。
  • 线程在进程内部运行,本质是在进程地址空间内运行。
  • 在Linux系统中,在CPU眼中,看到的PCB都要比传统的进程更轻量化。
  • 透过进程虚拟地址空间,可以看到进程的大部分资源,将进程资源合理分配给每个执行流,就形成了线程执行流。

线程是进程中的执行流 看待地址空间和页表:

  1. 地址空间是进程中能看到的资源窗口
  2. 页表决定进程能看到的资源窗口

**注意:**Linux没有线程,只是有进程模拟的

# 线程的优点和缺点

  1. 优点:
  • 与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多
  • 线程占用的资源要比进程少很多,能充分利用多处理器的可并行数量
  • 在等待慢速I/O操作结束的同时,程序可执行其他的计算任务
  • 计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现
  • I/O密集型应用,为了提高性能,将I/O操作重叠。线程可以同时等待不同的I/O操作。
  1. 缺点:健壮性较差

线程是进程的执行分支,线程出异常,就类似进程出异常,进而触发信号机制,终止进程,进程终止,该进程内的所有线程也就随即退出。

  • 合理的使用多线程,能提高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
[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

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

# 等待线程

一个线程被创建出来,也是需要被等待的。如果主线程不对新线程进行等待,那么这个新线程的资源也是不会被回收的,产生类似于“僵尸进程”的问题,也就是内存泄漏。

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

结果:

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

# 终止线程

  1. 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
  1. 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

# 线程分离

新创建的线程是默认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
上次更新: 2025/05/23, 14:23:42
进程信号
线程安全

← 进程信号 线程安全→

最近更新
01
线程安全
05-21
02
cmake教程
05-08
03
项目
05-07
更多文章>
Theme by Vdoing | Copyright © 2024-2025 京ICP备2020044002号-3 京公网安备11010502056119号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式