【Linux】线程安全

一.线程安全

概念:多个线程同时对临界资源进行访问,不会造成数据二义问题
实现:同步+互斥
同步:对临界资源访问的时序合理性
互斥:对临界资源同一时间访问的唯一性
线程间互斥的实现:互斥锁mutex

pthread_mutex_t mutex;  //定义互斥锁变量
pthread_mutex_init(pthread_mutex_t *restrict mutex,
        const pthread_mutexattr_t *restrict attr);  //对互斥锁变量进行初始化
        
int pthread_mutex_lock(pthread_mutex_t *mutex);  
int pthread_mutex_trylock(pthread_mutex_t *mutex);  //加锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);  //解锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);  //销毁

死锁:多个线程对资源进行竞争访问,但因推进顺序不当,导致相互等待,程序无法往下运行
死锁产生的必要条件
1.互斥条件:一个锁只有一个线程可以获取
2.不可剥夺条件:A加的锁,B不能解
3.请求与保持条件:A请求B,但获取不到,A也不释放已获得的资源
4.环路等待条件:A请求B,同时B请求A

避免死锁
1.破坏四个必要条件
2.加锁顺序一致
3.避免锁未释放的场景
4.资源一次性分配

二.线程同步

实现:等待+唤醒
操作条件不满足则等待,条件满足则唤醒

条件变量

条件变量实现同步:线程在对临界资源访问之前,先判断是否能够进行操作,若可以则直接操作,若不能,则条件变量提供等待功能,其他线程促使操作条件满足,然后唤醒条件变量等待队列上的线程

pthread_cond_t cond;  //定义条件变量
int pthread_cond_init(pthread_cond_t *restrict cond,
           const pthread_condattr_t *restrict attr);  //条件变量初始化
int pthread_cond_wait(pthread_cond_t *restrict cond,
              pthread_mutex_t *restrict mutex);  //用户在判断条件不满足的情况下提供等待功能
int pthread_cond_signal(pthread_cond_t *cond);  //用户在促使条件满足后,唤醒等待队列上的线程
int pthread_cond_destroy(pthread_cond_t *cond);  ///销毁

三.生产者与消费者模型

一个场景、两种角色、三种关系
解决问题:解耦和、支持忙闲不均、支持并发
生产者与生产者之间应保持互斥关系
消费者与消费者之间应保持互斥关系
生产者与消费者之间应保持同步+互斥关系

//基于BlockingQueue的生产者消费者模型
class BlockQueue
{
private:
	std::queue<int>_queue;
	int _capacity;//用于限制队列中最大节点数量
	pthread_mutex_t _mutex;
	pthread_cond_t _cond_product;
	pthread_cond_t _cond_consumer;
public:
	BlockQueue()
	~BlockQueue()
	bool QueuePush(int data);
	bool QueuePop(int &data);
};
POSIX信号量

功能:实现线程间同步与互斥
本质:计数器(资源技术–判断当前是否能对临界资源进行操作)+等待队列+等待+唤醒

原理:
互斥原理:当只具有0/1计数时,就可实现互斥
初始计数为1,表示当前只有一个线程能够获取资源,获取资源之后计数-1,临界资源操作完毕之后计数+1,并唤醒等待队列上的线程;计数为0,其他线程进行等待

同步原理:
1.对程序逻辑进行控制(对临界资源合理操作控制)
2.通过计数判断当前是否能够对临界资源进行操作,不能操作(计数<=0),则等待
3.其他线程促使条件满足后,计数+1,唤醒等待队列上的线程

与条件变量的区别:
1.信号量不需要搭配互斥锁使用
2.信号量本身的计数就是对临界资源进行判断,条件变量需要外部用户判断

sem_t sem;  //定义信号量
int sem_init(sem_t *sem, int pshared, unsigned int value);  //信号量初始化
int sem_wait(sem_t *sem);  //判断计数是否可以对临界资源进行操作
int sem_post(sem_t *sem);  //唤醒等待队列上的线程
int sem_destroy(sem_t *sem);  //销毁

使用信号量实现生产者与消费者模型

//基于唤醒队列实现
class RingQueue
{
private:
	std::vector<int>_queue;
	int _capacity;
	int _read;
	int _write;
	sem_t _lock;
	sem_t _idle_space;//生产者入队数据之前判断队列中是否有空闲空间,判断能否入队数据
	sem_t _data_space;//消费者获取数据之前判断有数据的空间有多少,判断能否获取数据
public:
	QueuePush(int data);
	QueuePop(int &data);
};

四.线程池

至少一个线程+任务队列(用于并发处理任务请求)
在程序初始化时,创建固定数量的线程(最大数量限制),从任务队列中获取任务,进行处理

作用:
1.避免为大量请求创建线程,导致瞬间资源耗尽,程序崩溃的问题
2.避免大量线程频繁创建销毁所带来的时间成本

实现:线程创建+线程安全的任务队列

功能:
1.线程池退出(线程池销毁条件是在所有线程退出之后进行销毁)
2.任务的安全入队
3.任务处理

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值