线程安全 | 互斥锁 条件变量 信号量

一.什么是线程安全?如何实现线程安全?

线程安全: 多个线程/进程对临界资源的访问操作是安全的

线程安全的实现:同步与互斥

互斥: 同一时间只有一个线程/进程能够访问临界资源,实现临界资源访问的安全

同步: 使多个线程/进程按照某种规则时序进行执行调度,实现对临界资源访问的合理性

互斥的实现: 互斥锁,信号量

同步的实现: 条件变量.信号量

二.互斥锁

pthread_mutex_t mutex; //定义互斥锁
 
pthread_mutex_init(pthread_mutex_t* mutex,pthread_mutexattr_t* attr); //互斥锁初始化
 
pthread_mutex_lock(pthread_mutex_t* mutex); //加锁:可以加锁则返回,不可则阻塞
 
pthread_mutex_trylock(pthread_mutex_t* mutex); //加锁:不能加锁则报错返回
 
pthread_mutex_unlock(pthread_mutex_t* mutex); //解锁
 
pthread_mutex_destroy(pthread_mutex_t* mutex); //销毁互斥锁释放资源

 互斥锁实现互斥的原理:

互斥锁本质就是一个只有0/1的计数器,用于标记临界资源的访问状态,每个线程在访问临界资源之前,都要先访问互斥锁,判断当前是否为可访问状态.

  • 如果可以访问,则将访问状态置为不可访问状态,然后去进行资源访问(这期间别人就无法访问),访问完毕后一定记得将访问状态置为可访问状态.
  • 如果不可以访问,则阻塞或者报错.

互斥锁实现自身原子操作(0/1之间转换)的原理:

为了实现互斥锁,大多数体系结构都提供了swap或exchange指令,该指令的作用是把寄存器和内存单元的数据进行交换.由于只有一条指令,则保证了其原子性.

寄存器:寄存器是中央处理器内的组成部分.寄存器是有限存贮容量的高速存贮部件,可以用来存储指令,数据,位址.在中央处理器的控制部件中,包含的寄存器有指令寄存器和程序计数器.在中央处理器的算数及逻辑部件中,包含的寄存器有累加器.

三.条件变量

pthread_cond_t cond; //定义条件变量变量
 
pthread_cond_init(pthread_cond_t* cond,pthread_condattr_t* addr); //初始化条件变量
 
pthread_cond_wait(pthread_cond_t* cond,pthread_mutex_t* mutex); //在一个线程不满足资源访问的情况下,是线程阻塞
pthread_cond_signal(pthread_cond_t* cond);  //在资源访问条件满足之后,唤醒阻塞的线程
 
pthread_cond_destroy(pthread_cond_t* cond); //销毁条件变量

条件变量实现同步:

条件变量提供了使线程阻塞以及唤醒阻塞的接口,但是具体什么时候阻塞线程,什么时候唤醒线程不确定.因此在使用条件变量实现同步,需要程序员自己合理的控制时序逻辑,只需要在合适的时候调用条件变量提供的阻塞/唤醒接口.

注意事项:

  • 条件判断必须使用循环操作

       有可能一个厨师做完饭,唤醒多个顾客.多个顾客被唤醒后,都会去加锁.假设A顾客加锁成功,吃           饭,吃完饭解锁.解锁后假设B顾客加锁成功,如果是IF语句,则B顾客在没有饭的情况下加锁,然后          成功吃饭.因此应该循环判断,加锁成功后,再次判断有没有饭,没有则重新休眠.

  • 多种角色应该使用多个条件变量,让不同的角色等待在不同的条件变量队列中,便于分角色唤醒,防止唤醒角色错误的情况

四.信号量

定义信号量 sem_t
初始化信号量  int sem_init(sem_t* sem,int pshared,int val);
pshared:如果为0则表示用于线程间;非0则表示用于进程间
val:用于初始化计数器的初值
返回值:成功则返回0 失败返回-1
在临界资源访问/获取之前进行P操作
sem_wait(sem_t* sem); 阻塞操作,表示当前计数小于等于0则阻塞
sem_trywait(sem_t* sem); 非阻塞,表示当前计数小于等于0则报错返回
sem_timedwait(sem_t* sem,struct timespec* timeout);  限时阻塞操作,表示在指定时间内若无法满足条件,等待超时后则报错返回
资源生产后,进行计数+1,唤醒阻塞V操作 int sem_post(sem_t* sem);
销毁信号量 sem_destroy(sem_t* sem);

class RingQueue{
    vector<int> _list;
    int _capacity;
    int _read_step; //标记当前读取数据位置的下标
    int _write_step;//当前写入位置的下标
 
    sem_t _sem_lock;//用于实现互斥
    sem_t _sem_idle; //空闲空间计数:生产者能否入队数据的依据 
    sem_t _sem_data; //数据资源个数:消费者能够获取数据的依据
};

本质:一个计数器+pcb等待队列

功能:实现进程或线程间的同步与互斥

互斥实现: 通过只有1个计数,表示资源只有一个,保证同一时间只有一个线程或者进程访问

同步实现: 通过计数器对资源进行计数,因此在资源获取时可以直接通过计数得知是否获取合理,从而进行对应操作

具体操作:

P操作: 在资源访问之前判断是否合理,计数-1.不合理则阻塞,合理则返回

V操作: 在生产资源之后,进行计数+1,唤醒一个阻塞的线程或进程

信号量与条件变量的不同在哪里?

  • 信号量通过自身计数实现判断,不需要搭配互斥锁
  • 条件变量需要程序员自己进行条件判断,需要搭配互斥锁使用

信号量与互斥锁的不同?

信号量可以实现互斥,但是更多的是用于同步的实现.

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值