1.作用
跟前面学习的进程间通信信号量的作用一样,协调多个线程对于共享资源的访问
2.三种方式协调多个线程对于共享资源的访问
互斥锁
条件变量
信号量
3.互斥锁
(1)原理:多个线程要使用同一个共享资源,此时我们就需要用互斥锁协调多个线程对于共享资源的访问(保证任意时刻有且仅有一个线程在访问共享资源)
比如:A和B两个线程,A线程上锁以后,A线程就能使用共享资源,此时B线程无法上锁,B线程会阻塞
等到A线程使用完共享资源之后,A线程解锁,解锁完毕,B线程才有机会上锁
特点:多个线程使用同一把锁,谁先上锁,会导致后面上锁的线程全部阻塞
上锁和解锁操作需要成对使用,如果你的代码只有上锁,忘记解锁,会导致死锁现象
死锁:线程上锁以后,忘记解锁,导致这把锁所有的线程都不能使用
常见的导致死锁的原因有如下两种
第一种:线程上锁以后,忘记解锁
解决方法:自己把解锁代码写上去即可
第二种:线程上锁以后,被取消了,导致来不及解锁
解决方法:一种是把线程设置成不可以取消
另外一种是使用push和pop函数,在线程取消的时候解锁
(2)相关的接口函数
第一个:创建初始化互斥锁
pthread_mutex_t 互斥锁
pthread_mutex_t mymutex;
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);
参数:mutex --》互斥锁
mutexattr --》互斥锁的属性,一般设置为NULL,表示使用默认属性
第二个:使用互斥锁
int pthread_mutex_lock(pthread_mutex_t *mutex); //上锁
参数:mutex --》你刚才初始化好的锁
int pthread_mutex_unlock(pthread_mutex_t *mutex); //解锁、开锁
参数:mutex --》你刚才初始化好的锁
第三个:销毁互斥锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);
参数:mutex --》你刚才初始化好的锁
注意:多个线程使用同一把锁,究竟谁先拿到锁(上锁)是随机(除非人为使用延时函数去干扰)
(3)总结互斥锁使用的模型
某个线程1
{
//上锁
这一部分代码就是互斥锁需要保护的共享资源,确保任意时刻只有一个线程在使用共享资源
//解锁
//延时一丢丢,防止自己刚解锁,立马自己上锁
}
某个线程2
{
//上锁
这一部分代码就是互斥锁需要保护的共享资源,确保任意时刻只有一个线程在使用共享资源
//解锁
//延时一丢丢,防止自己刚解锁,立马自己上锁
}
线程刚上锁,来不及解锁就被取消啦,导致死锁
#include "myhead.h"
//定义一个全局变量表示目前库存数量
int num=0;
//定义一个互斥锁
pthread_mutex_t mymutex;
//用代码来模拟生产和销售
//生产者
void *product(void *arg)
{
while(1)
{
//上锁
pthread_mutex_lock(&mymutex);
num++;
printf("生产了一件产品,目前产品数量是:%d\n",num);
sleep(5);
//解锁
pthread_mutex_unlock(&mymutex);
}
}
//销售者
void *sale(void *arg)
{
while(1)
{
//上锁
pthread_mutex_lock(&mymutex);
num--;
printf("销售了一件产品,目前产品数量是:%d\n",num);
sleep(5);
//解锁
pthread_mutex_unlock(&mymutex);
}
}
int main()
{
pthread_t id1,id2;
//初始化互斥锁
pthread_mutex_init(&mymutex,NULL);
//创建两个线程--》分别表示生产部门和销售部门
pthread_create(&id1,NULL,product,NULL); //生产部门
pthread_create(&id2,NULL,sale,NULL); //销售部门
sleep(2);
//干掉销售部门
pthread_cancel(id2);
//阻塞
pthread_join(id1,NULL);
pthread_join(id2,NULL);
return 0;
}
解决线程刚上锁,就被取消导致死锁的现象
#include "myhead.h"
//定义一个全局变量表示目前库存数量
int num=0;
//定义一个互斥锁
pthread_mutex_t mymutex;
void fun(void *arg)
{
printf("fun被调用了!\n");
//解锁
pthread_mutex_unlock(&mymutex);
}
//用代码来模拟生产和销售
//生产者
void *product(void *arg)
{
pthread_cleanup_push(fun,NULL);
while(1)
{
//上锁
pthread_mutex_lock(&mymutex);
num++;
printf("生产了一件产品,目前产品数量是:%d\n",num);
sleep(5);
//解锁
pthread_mutex_unlock(&mymutex);
}
pthread_cleanup_pop(0);
}
//销售者
void *sale(void *arg)
{
pthread_cleanup_push(fun,NULL);
while(1)
{
//上锁
pthread_mutex_lock(&mymutex);
num--;
printf("销售了一件产品,目前产品数量是:%d\n",num);
sleep(5);
//解锁
pthread_mutex_unlock(&mymutex);
}
pthread_cleanup_pop(0);
}
int main()
{
pthread_t id1,id2;
//初始化互斥锁
pthread_mutex_init(&mymutex,NULL);
//创建两个线程--》分别表示生产部门和销售部门
pthread_create(&id1,NULL,product,NULL); //生产部门
pthread_create(&id2,NULL,sale,NULL); //销售部门
sleep(2);
//干掉销售部门
pthread_cancel(id2);
//阻塞
pthread_join(id1,NULL);
pthread_join(id2,NULL);
return 0;
}
解锁完毕之后延时一丢丢防止自己给自己上锁
#include "myhead.h"
//定义一个全局变量表示目前库存数量
int num=0;
//定义一个互斥锁
pthread_mutex_t mymutex;
//用代码来模拟生产和销售
//生产者
void *product(void *arg)
{
while(1)
{
//上锁
pthread_mutex_lock(&mymutex);
num++;
printf("生产了一件产品,目前产品数量是:%d\n",num);
sleep(1);
//解锁
pthread_mutex_unlock(&mymutex);
//解锁完毕有两种可能性:
//第一种:生产者解锁完毕,生产者自己立马无耻地上锁
//第二种:销售者也有机会来抢这把锁
//究竟是哪一种情况,取决于cpu调度
usleep(100); //延时一丢丢,直接排除了第一种可能性,防止自己解锁自己马上上锁
}
}
//销售者
void *sale(void *arg)
{
while(1)
{
//上锁
pthread_mutex_lock(&mymutex);
num--;
printf("销售了一件产品,目前产品数量是:%d\n",num);
sleep(1);
//解锁
pthread_mutex_unlock(&mymutex);
//解锁完毕有两种可能性:
//第一种:销售者解锁完毕,销售者自己立马无耻地上锁
//第二种:生产者也有机会来抢这把锁
//究竟是哪一种情况,取决于cpu调度
usleep(100); //延时一丢丢,直接排除了第一种可能性,防止自己解锁自己马上上锁
}
}
int main()
{
pthread_t id1,id2;
//初始化互斥锁
pthread_mutex_init(&mymutex,NULL);
//创建两个线程--》分别表示生产部门和销售部门
pthread_create(&id1,NULL,product,NULL); //生产部门
//sleep(1); //北鼻手段
pthread_create(&id2,NULL,sale,NULL); //销售部门
//阻塞
pthread_join(id1,NULL);
pthread_join(id2,NULL);
return 0;
}