一、线程的概念
线程是进程的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程,一个进程中可以有多个线程
多个线程共享同一个进程的资源,每个线程参与操作系统的统一调度
可以简单理解: 进程 = 进程资源 + 主线程 + 子线程+......
进程与线程的区别:
1、线程包含于进程,多个进程拥有独立的内存空间
2、线程间通讯方式简单,进程间通讯方式复杂
并发操作,线程比进程更节约资源
联系紧密的任务在并发时优先选择多线程,如果任务之间比较独立,在并发时建议选择多进程。
二、线程的资源
共享进程的资源
同一块地址空间
文件描述符表
每种信号的处理方式(如:SIG_DFL,SIG_IGN或者自定义的信号优先级)
当前工作目录
用户id和组id
独立的资源
线程栈
每个线程都有私有的上下文信息。
线程ID
寄存器的值
errno变量
信号屏蔽字以及调度优先级
三、线程的命令
pidstat命令可查看对应进程的线程
top命令 top -H -p pid
可查看某一进程下的线程(需要知道进程的pid)
ps - T 可以查看某个进程下的所有线程
四、创建线程
1、创建线程使用 pthread_create 函数
一旦子线程创建成功,则会被独立调度执行,并且与其他线程并发执行
在编译时需要链接 -lpthread
2、线程退出使用 pthread_exit 函数
当主线程调用pthread_exit函数时,
3、进程不会结束,也不会导致其他子线程退出
任何线程调用exit函数会让进程结束
4、线程等待调用 pthread_join函数,会阻塞调用线程
5、线程分为可结合的与可分离的
可结合
可结合的线程能够被其他线程收回其资源和杀死;在被其他线程回收之前,它的存储器资源(如栈)是不释放的。
线程创建的默认状态为可结合的,可以由其他线程调用 pthread_join函数等待子线程退出并释放相关资源
可分离
不能被其他线程回收或者杀死的,该线程的资源在它终止时由系统来释放。
线程分离调用 pthread_detach 函数
五、创建多个线程
线程一般由主线程统一创建,并等待释放资源或分离线程,不要递归创建
1. 多个线程如果任务相同,则可以使用同一个线程执行函数
2. 多个线程如果任务不同,则可以使用不同的线程执行函数
六、线程间通信
1、主线程向子线程传递参数
通过 pthread_create 函数的第4个参数 arg 进行传递
2、子线程给主线程传递参数
子线程给主线程传参的方式如下:
在子线程将需要返回的值存储在 pthread_exit 函数中的 retval 参数中
在主线程中通过 pthread_join 函数的第2个参数 retval 得到返回, pthread_join 函数会将线程的1返回值(指针)保存到 retval 中
七、线程互斥锁
1、线程的主要优势在于能够通过全局变量来共享信息,不过这种便捷的共享是有代价的:
必须确保多个线程不会同时修改同一变量
某一线程不会读取正由其他线程修改的变量,实际就是 不能让两个线程同时对临界区进行访问
线程互斥锁则可以用于解决多线程资源竞争问题
2、 线程互斥锁工作机制
当线程A获得锁,另外一个线程B在获得锁时则会阻塞,直到线程A释放锁,线程B才会获得锁。
2. 线程互斥锁工作原理
本质上是一个pthread_mutex_t类型的变量,假设名为 v
当 v = 1,则表示当前临界资源可以竞争访问 ,得到互斥锁的线程则可以访问,此时 v = 0
当 v = 0,则表示临界资源正在被某个线程访问,其他线程则需要等待
3. 线程互斥锁的特点
互斥锁是一个 pthread_mutex_t类型的变量,就代表一个互斥锁
如果两个线程访问的是同一个 pthread_mutex_t 变量,那么它们访问了同一个互斥锁
对应的变量定义在 pthreadtypes.h 头文件中,是一个共用体中包含一个结构体
八、线程互斥锁的初始化
1、静态初始化
定义 pthread_mutex_t 类型的变量,然后对其初始化为 PTHREAD_MUTEX_INITIALIZER
pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER
2、动态初始化
动态初始化主要涉及两个函数 pthread_mutex_init 函数 与 pthread_mutex_destroy 函数
3、
线程互斥锁的操作主要分为 获取锁(lock) 与 释放锁(unlock)
获取锁的函数:pthread_mutex_lock
释放锁的函数:pthread_mutex_unlock
八、线程同步
一、线程同步的概念
线程同步:是指在互斥的基础上,通过其它机制实现访问者对 资源的有序访问。
条件变量:线程库提供的专门针对线程同步的机制
线程同步比较典型的应用场合就是 生产者与消费者
二、生产者与消费者模型原理
1. 在这个模型中,包括生产者线程与消费者线程。通过线程来模拟多个线程同步的过程
2. 在这个模型中,需要以下组件
仓库 : 用于存储产品,一般作为共享资源
生产者线程 : 用于生产产品
消费者线程 : 用于消费产品
3. 原理
当仓库没有产品时,则消费者线程需要等待,直到有产品时才能消费
当仓库已经装满产品时,则生产者线程需要等待,直到消费者线程消费产品之后
九、条件变量
条件变量的本质为 pthread_cond_t 类型的变量,其他线程可以阻塞在这个条件变量上,或者唤醒阻塞在这个条件变量上的线程
条件变量的初始化分为静态初始化与动态初始化
1. 静态初始化
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
2. 动态初始化
pthread_cond_init 函数
pthread_cond_destroy函数
3、条件变量的阻塞与唤醒
step 1 :消费者线程判断消费条件是否满足(仓库是否有产品),如果有产品则可以消费,然后解锁
step 2 :当条件满足时(仓库产品数量为0),则调用 pthread_cond_wait 函数,这个函数具体做的事
情如下:
在线程睡眠之前,对互斥锁解锁
让线程进入到睡眠状态
等待条件变量收到信号时,该函数重新竞争锁,并获取锁
step 3 :重新判断条件是否满足,如果满足,则继续调用 pthread_cond_wait 函数
step 4 :唤醒后,从 pthread_cond_wait 返回,条件不满足,则正常消费产品
step 5 :释放锁,整个过程结束
4、 pthread_cond_wait
作用: 阻塞线程,等待唤醒
条件变量需要与互斥锁结合使用,先获得锁才能进行条件变量的操作
调用函数后会释放锁,并阻塞线程
一旦线程唤醒,需要重新竞争锁,重新获得锁之后,pthread_cond_wait 函数返回
5、 pthread_cond_signal和pthread_cond_broadcast
pthread_cond_signal 函数主要适用等待线程都在执行完全相同的任务
pthread_cond_broadcast 函数主要适用等待线程都执行不相同的任务
条件变量并不保存状态信息,只是传递应用程序状态信息的一种通讯机制,发送信号时若无任何线
程在等待该条件变量,则会被忽略
条件变量代表是一个通讯机制,用于传递通知与等待通知,用户可以设定条件来发送或者等待通知
6、 为什么条件变量需要与互斥锁结合起来使用?
答:防止在调用 pthread_cond_wait 函数等待一个条件变量收到唤醒信号,另外一个线程发送信号在第一个线程实际等待它之前,也就是说线程还没有完全进入到睡眠状态,其他线程发送唤醒信号
7、在判断条件时,为什么需要使用 while(number == 0),而不是 if()
答:防止虚假唤醒。
能够唤醒的情况如下:
被信号唤醒,并非由条件满足而唤醒
条件变量状态改变时,一次唤醒多个线程,但是被其他线程先消费完产品,等到当前线程执行时,条件已经不满足