- 线程的同步与互斥
1.1 基本概念
【1】临界资源:进程或线程之间共享的资源,但同一时间只能被一段指令序列所占用的资源就是所谓的临界资源。
典型的临界资源比如物理上的打印机,或是存在硬盘或内存中被多个进程/线程所共享的一些
变量和数据等。这类资源如果不被看成临界资源加以保护,那么很有可能因为访问冲突造成数据
错乱的问题。
【2】临界区:程序内访问临界资源的代码序列被称为临界区。
【3】互斥:是指同时只允许一个访问者对临界资源进行访问,具有唯一性和排它性。但互斥无法限制
访问者对资源的访问顺序,即访问是无序的。
【4】同步:是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在
大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。
1.2 互斥锁(实现互斥)
1.2.1 简介
我们对线程的互斥主要介绍NPTL库自带的
pthread_mutex_t互斥锁只有两种状态:上锁和解锁
上锁(拥有互斥锁): 一个线程在访问临界资源前必须先获得获得互斥锁(上锁),如果一个线程对一个已经上锁互锁锁再试图上锁,就会陷入阻塞直到某个线程
释放为止
解锁(释放互斥锁): 线程访问完临界资源之后必须释放互斥锁
1.2.2 操作函数
【1】初始化 (互斥锁必须初始化后才可以使用)
a.动态初始化
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
函数参数:
[1] mutex : 互斥锁
[2] attr : 互斥锁的属性(NULL 使用默认属性)
返回值:
成功 返回 0
失败 返回 errnum
b.静态初始化
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
【2】销毁互斥锁(对于互斥锁的所有操作完成之后)
int pthread_mutex_destroy(pthread_mutex_t *mutex);
函数参数:
mutex : 互斥锁
返回值:
成功 返回 0
失败 返回 errnum
【3】上锁
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
函数参数:
mutex : 互斥锁
返回值:
成功 返回 0
失败 返回 errnum
【4】解锁
#include <pthread.h>
int pthread_mutex_unlock(pthread_mutex_t *mutex);
函数参数:
mutex : 互斥锁
返回值:
成功 返回 0
失败 返回 errnum
1.3 信号量/信号灯 (semaphore)(可以实现同步与互斥)
1.3.1 信号量的概念:
信号量是用来解决进程/线程之间的同步与互斥问题的一种通信机制,它表示代表某一类资源,
其值表示系统中该资源的数量(可用的)。
信号量的数据类型: sem_t
1.3.2 信号量的操作
b. P操作(申请资源)
if (信号量的值 > 0) {
申请资源的任务继续;信号量值–;
} else {
申请资源的任务阻塞
}
c. V操作(释放资源)
if (没有任务在等待该资源){
信号量的值加一;
}else{
唤醒第一个等待的任务,让其继续运行;
}
注意:一个任务申请资源时有可能被阻塞,一个任务释放资源时一定不会被阻塞
1.3.3 原子操作 (atomic operation)
所谓原子操作是指不会被线程调度机制打断的操作;
这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)。
互斥锁 上锁和解锁的操作是原子的
P&V操作也是原子操作
1.3.4 信号量的类型
POSIX 信号量
(1)无名信号量 *(线程间的同步、互斥)/进程间同步互斥(共享内存)
(2)有名信号量 用于进程间同步互斥、线程
System V 信号量
主要用于进程之间的同步与互斥
1.3.5 POSIX 无名信号量API
【1】信号量的初始化
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
函数参数:
[1] sem : 信号量
[2] pshared : 表示信号量的用户
0 代表在线程中使用
!0 代表在进程中使用
[3] value : 信号量的初始值
返回值:
成功 返回 0
失败 返回 -1
【2】销毁信号量
#include <semaphore.h>
int sem_destroy(sem_t *sem);
函数参数:
[1] sem : 信号量
返回值:
成功 返回 0
失败 返回 -1
【3】p操作
#include <semaphore.h>
int sem_wait(sem_t *sem);
函数参数:
[1] sem : 信号量
返回值:
成功 返回 0
失败 返回 -1
【4】v操作
#include <semaphore.h>
int sem_post(sem_t *sem);
函数参数:
[1] sem : 信号量
返回值:
成功 返回 0
失败 返回 -1
1.3.6 信号量实现同步\互斥
【1】实现互斥
开始
|
初始化信号量值为1
| |
p操作 p操作
| |
线程1执行 线程2执行
| |
V操作 v操作
|
结束
【2】实现同步
开始
|
初始化信号量
sem1为 1,sem2为0
| |
p操作sem1 p操作sem2
| |
线程1执行 线程2执行
| |
V操作sem2 v操作sem1
|
结束
开始
|
初始化信号量
sem1为 1,sem2为0,sem3 为0
| | |
p操作sem1 p操作sem2 p操作sem3
| | |
线程1执行 线程2执行 线程3
| | |
V操作sem2 v操作sem3 v操作sem1
|
结束