记录肖堃老师讲解的linux线程
1. linux系统中多任务(进程/线程)之间的关系
1> 独立:仅竞争CPU资源
2> 互斥:竞争除CPU外的其他资源
3> 同步:协调彼此运行的步调,保证协同运行的各个任务具有正确的执行次序
4> 通信:数据共享,彼此间传递数据或信息,以协同完成某项工作
2. 线程的数据共享
线程间共享的数据和资源:进程代码段、进程中的全局变量(数据段)、进程打开的文件......
每个线程私有的数据和资源:线程ID,线程上下文(一组寄存器值的集合)、线程局部变量(存储在栈中)。
因此,线程之间可以通过进程中的全局变量,非常容易的实现通信和数据共享。
3. 任务的互斥关系
任务互斥-资源共享关系(间接相互制约关系)
任务本身之间不存在直接联系。一个任务正在使用某个系统资源,另外一个想用该资源的任务就必须等待,而不能同时使用
例如:全局变量存储在进程数据段中,被线程所共享。线程对全局变量的访问,要经历三个步骤:
1)将内存单元中的数据读入寄存器
2)对寄存器中的值进行运算
3)将寄存器中的值写回内存单元
如上图所示:
两个线程A和B,共享同一个全局变量 i,两个线程对 i 都实现i+1的操作,在某种特定极端情况下,会产生一些意料不到的效果。
1> 线程A先执行,将内存中的 i 值读入到寄存器当中,此时i的值为5
2> 线程A对寄存器中的 i 值进行 加1 运算,此时寄存器值为6。同时线程B并发开始运行。由于线程A还没有将寄存器的值(register=6)写回内存单元,
此时,线程B从内存中读取的 i 的值还是5。
3> 线程A将寄存器值写回内存(register=6)。线程B并发执行 i+1, 由于每个线程中,寄存器是私有的。因此,此时线程B中寄存器值执行的是(5+1=6)。
4> 线程B将寄存器值写回内存(register=6)
以上情况发现,本来的意图是 线程A执行 i+1后, 线程B执行再 i+1, 但是因为两个线程之间没有对共享资源访问的保护机制,所以导致对全局变量修改的丢失。
4. 临界资源
针对上面提到的情况,操作系统提出了临界资源的概念
临界资源:在一段时间内只允许一个任务(线程/进程)访问的资源。各个任务间应采取互斥方式,实现对资源的共享。
共享变量,打印机等属于临界资源。
访问临界资源的那段代码被称为临界区
互斥量的作用:确保同一时间里 只有一个线程访问临界资源或进入临界区
互斥量(mutex)本质上是一把锁
1)在访问临界资源前,对互斥量进行加锁
2)在访问完成后对互斥量进行解锁
3)对互斥量加锁后,任何其他试图对互斥量加锁的线程将会被阻塞,直到互斥量被解锁为止
5. 互斥量使用
1)定义互斥量变量
pthread_mutex_t mutex;
2)初始化互斥量
3)访问临界资源前对互斥量加锁
4)访问临界资源后对互斥量解锁
5)销毁互斥量变量
1)互斥量初始化
头文件:pthread.h
静态初始化:
pthread_mutex_t mlock = PTHREAD_MUTEX_INITIALIZER
动态初始化:
int pthread_mutex_init( pthread_mutex_t *mutex, const pthread_mutexattr_t *attr )
参数与返回值:
mutex - 指向互斥量的指针
attr - 设置互斥量的属性,通常可采用默认属性,传入空指针(NULL)
成功返回0,出错返回错误码
2)互斥量销毁: 互斥量使用完毕后,必须要对互斥量进行销毁,以释放资源
int pthread_mutex_destory( pthread_mutex_t *mutex )
参数与返回值:
mutex - 指向互斥量的指针
成功返回0,出错返回错误码
3)在对临界资源访问之前和访问之后,需要对互斥量进行加锁和解锁
int pthread_mutex_lock( pthread_mutex_t *mutex )
int pthread_mutex_unlock( pthread_mutex_t *mutex )
参数与返回值:
mutex - 指向互斥量的指针
成功返回0,出错返回错误码
当调用pthread_mutex_lock时,若互斥量已被加锁,则调用线程将被阻塞,直到可以完成加锁操作(其他线程解锁后)为止
4)互斥量非阻塞加锁
pthread_mutex_trylock( pthread_mutex_t *mutex )
调用该函数时,若互斥量未加锁,则对该互斥量进行加锁,返回0;若互斥量已加锁,则函数直接返回错误码EBUSY(不会阻塞调用线程)