线程的概念
回顾进程
在介绍线程之前,我们先来回顾下进程,进程的定义就是指一个具有独立功能的程序在某个数据集上的一次动态执行的过程,是系统进程资源分配和调度的基本单元。 一次任务的运行可以并发激活多个进程,这些进程相互合作完成该任务的一个最终目标。 操作系统对进程的描述:PCB(进程控制块)Linux下的进程描述——task_struct。
那么线程是什么呢?
- 线程是进程中的一条执行流,Linux下的线程是用进程的PCB模拟的,所以Linux下的线程也叫轻量级进程。 进程是资源分配的基本单位, 那么线程就是CPU调度的基本单位。
- 一个进程至少有一个线程,因此我们的进程其实就是线程组。进程id = 线程组id,所以才说Linux下的进程是线程组,资源分配的基本单位,并且进程中的线程共享大部分进程的资源。
- Linux下的线程共用进程的虚拟地址空间,与进程内的其他线程共享进程的资源,共享代码段,数据段。
- 文件描述符表,信号处理方式,工作目录用户id
- 线程不仅共享进程的这些资源,并且还独自有一些资源:栈,上下文数据。
有了进程为什么还要线程呢?
线程的优缺点正好说明了我们操作系统为什么还要线程。
优点:
- 线程的创建和销毁成本更低。
- 线程的调度切换成本也会更低
- 线程间通信更加方便
- 线程执行的力度更加细致
缺点- 缺乏访问控制:进程是访问的基本粒度,在一个线程中调用可能会对整个进程造成影响
- 多个线程对临界资源进行操作时会造成数据混乱
- 性能损失:一个很少被外部事件阻塞的计算密集型线程往往无法与其它线程共享一个处理器。如果密集型线程数量比可用的处理器多,那么可能会有较大的性能损失,这里的性能损失指的是增加额外的同步和调度开销,而可用的资源不变。
- 调试难度大大提高:编写与调试一个多线程程序比单线程程序困难的多。
即便是线程有一些缺点,但它的作用依然非常强大,任然引用与一些项目中。
进程的一些特征:
我们知道,进程有进程的标识符pid,那么线程也有自己的标识符tid
线程的基本操作
1.pthread_create函数
功能:创建线程
1 | int pthread_create(pthread_t *tid, const pthread_attr_t *attr, void *(*func)(void *), void *arg); |
2.pthread_join函数 功能:等待一个线程终止
1 | int pthread_join(pthread_t *tid, void **status); |
3.pthread_self函数 功能:返回线程ID
1 | int pthread_self(void); |
4.pthread_detach函数 功能:线程分离
1 | int pthread_detach(pthread_t tid); |
5.pthread_exit函数 功能:线程终止
1 | int pthread_exit(void **status); |
下面我们演示一个线程的创建
1 |
|
下面是一个线程分离演示
1 |
|
多线程
一个线程的作用不大,但是一堆线程能干大事,了解王者荣耀的人可能会知道,王者荣耀采用了多线程开发技术,多线程模式,这个模式的作用可以让玩家在团战中帧率更高,打起来更加的流畅,这就是多线程的一个好处。
线程安全问题
因为线程是CPU调度的基本单位,因此多个线程就有可能同时争抢临界资源,那么这种情况就有可能导致数据的二义性。打个比方:当两个线程同时向一个文件修改数据时,那么这个文件的数据到底让那个线程修改?这就是一个线程安全问题。
为了解决线程安全问题,我们引入了线程的同步与互斥
互斥:保证数据同一时间唯一的访问,那么我们就可以用一个锁来锁住当前线程,不让其它线程进行访问,即互斥锁
1
2
3
4
5
6pthread_mutex_init //初始化
pthread_mutex_destroy
pthread_mutex_lock
pthread_mutex_trylock
pthread_mutex_unlock
pthread_mutex_timedlock //指定一段时间内获取锁死锁:在使用互斥锁的同时,有可能会发生死锁,在前面有关于死锁问题的文章,这里就不做赘述了
同步:保证对临界资源访问的时序性,即我们需要条件变量通知线程或等待线程,满足操作条件,才可以操作,不满足则需要等待,而条件满足就需要其它线程修改条件,并且通知一下等待的进程
1
2
3
4
5
6pthread_cond_init //初始化
pthread_cond_destroy
pthread_cond_broadcast //唤醒多个线程,广播唤醒
pthread_cond_wait //等待
pthread_cond_timedwait
pthread_cond_signal // 唤醒第一个等待的线程,通知一个线
下面演示一个互斥锁使用场景,抢票
1 |
|