目录
前言
线程互斥指的是在多个线程间对临界资源进行争抢访问时有可能会造成数据二义,因此通过保证同一时间只有一个线程能够访问临界资源的方式实现线程对临界资源的访问安全性。
互斥锁
互斥锁可以实现线程间的互斥。(线程间同步的实现是条件变量和信号量)
本质:互斥锁的本质是01计数器,用于表示当前是否可以加锁。
实现原理:
加锁:访问资源前会进行加锁操作(加锁后需要在线程退出的地方解锁)
解锁:修改互斥锁的计数器修改为可加锁状态,唤醒所有阻塞的线程。
通过一个线程加锁后,其他线程因为无法加锁成功而无法访问资源,来实现同一时间的唯一访 问保证安全。
锁就是mutex,是个01计数器
·当mutex=0表示已经有执行流加锁成功,资源处于不可访问
·当mutex = 1表示未加锁,资源可访问
在进行加锁操作时我们一般都是先将cpu里的mutex修改为1表示解锁,然后再将内存中的数据传入cpu进行处理,传入数据完后再给内存中的mutex修改为0表示加锁(即这块内存中的数据暂时无法被其他线程访问)
但是上面所设想的流程有个漏洞:如果在数据传入阶段切换线程,那么此时内存中的mutex还没来得及修改为0(没来得及加锁),那么最终结果可能会出错。
所以实际上的加锁操作是exchange(交换cpu指定寄存器与内存中的数据)。也就是说交换操作就是将上面的三步走合并成了一步完成,这样就不会出现上面的错误。
实际流程:
1、先将指定寄存器中的值修改为0
2、寄存器与内存进行数据互换
3、判断是否符合获取锁的条件,或是否符合加锁条件。
死锁
概念:死锁是一种状态,是一种因为资源争抢不当导致的程序流程卡死无法继续推进的状态。
产生死锁的4个必要条件:
1、互斥条件
2、不可剥夺条件
3、请求与保持条件
4、环路等待条件
死锁出现的一种情况如下:
死锁的预防:破坏死锁产生的必要条件。
1、使用非阻塞加锁,比如加了A锁后,继续加其他锁,若无法加锁成功则释放A锁(请求不到新的,则释放已有的——破坏请求与保持条件)
2、加锁顺序保持一致(避免出现线程1加了A锁后请求B锁,线程2加了B锁后请求A锁——尽可能破坏环路等待条件)
死锁的避免方法:银行家算法、死锁检验算法等。
相关接口
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutex_attr_t *mutexattr);
初始化互斥锁
mutex:互斥锁
mutexattr:互斥锁的创建方式(通常设置为NULL,使用默认属性)
int pthread_mutex_destroy(pthread_mutex_t *mutex);
销毁互斥锁,成功返回0,失败返回错误编码
int pthread_mutex_lock(pthread_mutex_t *mutex);
上锁,上锁失败时阻塞
int pthread_mutex_trylock(pthread_mutex_t *mutex);
上锁,上锁失败时返回错误信息
int pthread_mutex_unlock(pthread_mutex_t *mutex);
解锁,成功返回0,失败返回错误编码
模拟多线程抢票(需加锁)
定义5个线程抢票:
注意(在Xshell7环境下)编译时要加上-std=gnu++0x(代码中使用了nullptr),以及-lpthread(线程操作)
#include <iostream>
#include <string>
#include <pthread.h>
#include <unistd.h>
class Tickets{ //定义一个类
private:
int tickets;
pthread_mutex_t mutex; //类中有锁
public:
Tickets(int ticket):tickets(ticket){
pthread_mutex_init(&mutex,nullptr);
}
bool GetTickets(){ //判断是否有票
bool flag=true;
pthread_mutex_lock(&mutex);
if(tickets>0){
usleep(1000); //休眠1000微秒
tickets--;
printf("线程[%lu]抢到了票:%d\n",pthread_self(),tickets);
}
else{
printf("非常抱歉,票已被抢光!\n");
flag=false;
}
pthread_mutex_unlock(&mutex);
return flag;
}
~Tickets(){
pthread_mutex_destroy(&mutex);
}
};
void* SnatchTickets(void* argc){ //抢票
Tickets* t=(Tickets*)argc;
while(1){
if(!t->GetTickets()){
break;
}
else{
continue;
}
}
return (void*)0;
}
int main(){
Tickets* t = new Tickets(100); //100张票
pthread_t tid[5]; //定义5个线程抢票
for(int i=0;i<5;++i){
pthread_create(tid+i,NULL,SnatchTickets,(void*)t);
}
for(int i=0;i<5;++i){
pthread_join(tid[i],NULL);
}
return 0;
}
以上。