Linux线程

目录

一、引言

二、线程概念

三、线程相关函数

[3.1 线程创建](#3.1 线程创建)

[3.2 线程退出](#3.2 线程退出)

[3.3 线程等待](#3.3 线程等待)

[3.4 线程分离](#3.4 线程分离)

[3.5 线程ID](#3.5 线程ID)

四、进程与线程的对比

[4.1 线程的优点](#4.1 线程的优点)

[4.2 线程的不足](#4.2 线程的不足)

五、线程试题代码

六、结语


一、引言

本文将介绍线程的基本概念、如何创建线程和线程切换与进程切换的开销,在此基础上理解为什么有了进程还要引入线程,最后完成一道综合题来熟悉线程的相关操作。

题目如下:

1)在主线程中启动线程1,打印"This is thread1!"。

2)在主线程中启动线程2,打印"This is thread2!"。

3)线程1需要等待线程2结束,打印"Thread1 wait thread2 succeed!"。

4)主线程回收线程1,打印"Main thread wait thread1 succeed!"后退出。

在文章结束后,这道题大家就都会做了。

二、线程概念

进程里的一个执行流就叫做线程(thread),它是操作系统能够调度的最小单位。在接触线程前,我们所写的代码全部都是单线程,也就是说一个进程里只跑了一个线程,这个线程就叫做主线程。

三、线程相关函数

下面的所有函数都是pthread库提供的,所以这些函数有一个共同特点------函数名都以pthread开头。在编译的时候,要带上"-lpthread"选项,指定要链接的库名称。

3.1 线程创建

#include <pthread.h>

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,

void *(*start_routine)(void*), void *arg);

  • pthread_t *thread:指向线程ID的指针。
  • pthread_attr_t *attr:通常设为空,表示使用默认属性。
  • void *(*start_routine)(void*):参数为void*,返回值也为void*的函数指针,线程函数的入口点,表示线程从这个函数开始执行。
  • void *arg:传递给start_routine的参数。
  • 返回值:线程创建成功返回0,失败返回错误码。

这个函数是专门用来创建线程的。

3.2 线程退出

#include <pthread.h>

void pthread_exit(void *retval);

  • void *retva:线程入口函数的返回值。

哪个线程调用这个函数,该线程就会退出。

3.3 线程等待

#include <pthread.h>

int pthread_join(pthread_t thread, void **retval);

  • pthread_t thread:要等待的线程ID,即创建线程时的第一个参数对应的值。
  • void **retval:用于存储线程函数的返回值,返回值为一级指针,所以用二级指针来指向。
  • 返回值:成功返回0,失败返回错误码。

已经退出的线程,仍然占用进程的地址空间,而且新创建的线程也不会去复用这块空间,所以需要将这块空间回收,pthread_join函数干的就是这个。哪个进程调用pthread_join函数,该进程执行到这个函授后就会被挂起,直到指定要等待的线程终止,回收它的空间后,才能继续向下执行。

3.4 线程分离

#include <pthread.h>

int pthread_detach(pthread_t thread);

  • pthread_t thread:要分离的线程ID,即创建线程时的第一个参数对应的值。
  • 返回值:成功返回0,失败返回错误码。

默认情况下,我们创建的新线程是要被join的,否则会占用进程内部的地址空间。如果我们不关心线程的返回值,我们就可以调用pthread_detach函数,将线程设置为分离状态,告诉操作系统,在该线程退出后,帮我释放该线程持有的资源。在进程部分,我们可以将创建出来的子进程托孤,交给操作系统来管理,在该进程退出后释放它占有的资源。

3.5 线程ID

#include <pthread.h>

pthread_t pthread_self(void);

  • 返回值:线程ID。

哪个线程调用这个函数,就返回该线程对应的ID。

四、进程与线程的对比

已经有了进程,为何引入线程?

4.1 线程的优点

1)创建一个新线程的代价要比创建一个新进程小得多。

一个进程内部,可以有多个执行流,也就是可以有多个进程。这些线程共用进程的地址空间、页表等。因为线程不需要开辟新的地址空间、页表等,所以创建一个线程的代价当然要比创建一个进程的代价低得多。

2)与进程切换相比,线程切换的代价要少很多。

每个进程都是有自己独立的地址空间的,切换的时候需要保存和恢复大量的上下文信息,比如进程的代码段、数据段、打开的文件描述符等。

进程的切换会扰乱缓存机制。以进程A切换到进程B为例,一旦切换,CPU的高速缓存基本作废。因为每个进程它都有独属于自己的代码和数据,我进程B凭什么要去访问你进程A的代码和数据(非共享资源)。在进程切换的时候,快表TLB也要被刷新,快表里面存的就是一些虚拟地址到物理地址的映射,内存管理单元MMU首先拿虚拟地址去查快表,如果命中了,那么就直接找到了想要的数据在内存中的位置,而不用在经过页表的转化。所以,快表刷新以后,在一段时间内,CPU访问内存的效率是非常的低。

而线程的切换,只需要保存少量的寄存器状态(线程自己的上下文),无需切换地址空间或刷新TLB。不同的线程也很有可能要访问相同的数据或者代码,所以缓存不会失效。

3)可充分利用多核CPU,更好挖掘硬件潜力。

多线程允许单个进程内并发执行多个任务,特别适合多核CPU环境。例如在GUI应用中,主线程保持界面响应,工作线程处理后台计算,避免界面"卡顿"。

4.2 线程的不足

1)健壮性降低。

一个进程内部,有多个线程,一个线程出问题,其他线程也要受到牵连。比如,进程内部有个线程A,它越界访问了或者有除零行为,那么操作系统就会识别到异常,发出干掉这个进程的信号,这个信号干掉的可不只有线程A,而是整个进程。

2)编程难度提高。

因为多线程容易出问题,自然需要考虑的更多。

到这,本节一开始就提出的问题便有了答案。

五、线程试题代码

经过上面的学习,我们对线程有了一定的认识,下面来完成一开始就提出的问题。

cpp 复制代码
#include <iostream>
#include <pthread.h>

pthread_t thread1, thread2;

void* func1(void* arg) {
    std::cout << "This is thread1!" << std::endl;
    pthread_join(thread2, nullptr); //等待thread2结束
    std::cout << "Thread1 wait thread2 succeed!" << std::endl;
    return nullptr;
}
void* func2(void* arg) {
    std::cout << "This is thread2!" << std::endl;
    return nullptr;
}
int main()
{
    pthread_create(&thread1, nullptr, func1, nullptr);
    pthread_create(&thread2, nullptr, func2, nullptr);

    pthread_join(thread1, nullptr);//等待thread1结束
    std::cout << "Main thread wait thread1 succeed!" << std::endl;

    return 0;
}

六、结语

本篇是关于线程的最基本理解与操作,除此之外还有线程同步和互斥等进阶玩法。


完~

相关推荐
郝学胜-神的一滴2 小时前
深入浅出 C++20:新特性与实践
开发语言·c++·程序人生·算法·c++20
格林威2 小时前
Linux使用-MySQL的使用
linux·运维·人工智能·数码相机·mysql·计算机视觉·视觉检测
程序员TNT2 小时前
Shoptnt 促销计算引擎详解:策略模式与责任链的完美融合
linux·windows·策略模式
汉克老师2 小时前
第十四届蓝桥杯青少组C++选拔赛[2023.1.15]第二部分编程题(2 、寻宝石)
c++·蓝桥杯·蓝桥杯c++·c++蓝桥杯·蓝桥杯选拔赛
大锦终2 小时前
【Linux】进程间通信
linux·运维·服务器·c++
闪电麦坤952 小时前
C/C++项目练习:命令行记账本
开发语言·c++
kyle~2 小时前
python---PyInstaller(将Python脚本打包为可执行文件)
开发语言·前端·python·qt
望获linux2 小时前
【实时Linux实战系列】规避缺页中断:mlock/hugetlb 与页面预热
java·linux·服务器·数据库·chrome·算法
菜就多练,以前是以前,现在是现在2 小时前
Codeforces Round 1048 (Div. 2)
数据结构·c++·算法