互斥锁:用于协调多线程访问同一共享资源的完整性;保证了同一时刻不存在多个线程同时访问一块共享资源的数据;
(1)当一个线程给这个资源上锁且还没有解锁前,其他线程是无法对该资源进行访问的
(2)当一个线程对一块共享资源进行访问时上了锁,但是其他资源此时也想进行访问,则会挂起直到当前访问资源的线程解锁后,才会被唤醒再去访问
(3)每次线程进行访问时上锁后,访问结束后一定记得解锁,否则会导致形成死锁,其他线程无法上锁,也无法访问
就如:现在有一个资源,但是呢这个资源是很多线程都可以去访问的,我们假设这块内存的资源是一栋房子;然后这个时候为了防止一下子每个人都涌进去出现一块内存同时被多个线程操作,出现数据混乱等问题,我们就在它门口装上一把锁,这把锁只有一条钥匙,当线程1抢到钥匙后就可以把门给上锁不让其他线程进来,这个时候其他线程没办法只能在门口睡觉直到线程1出来后把门解锁了再把钥匙丢出去让大家抢,当然线程1也是可以再去参与抢钥匙的,如果再次被线程1抢到,线程1就会再次进去;但是也存在线程1出来后把把门的锁解开钥匙就一直拿在手里,这样则会形成死锁,然后线程1每次访问都要上一次锁的,但是本来它就没有解锁,因此因为有一把琐了,它再想上锁是不行的,这样就导致了大家都不能进去形成死锁了
互斥锁常用函数:
//定义互斥锁时,系统宏定义了PTHREAD_MUTEX_INITIALIZER这个宏用于静态初始化互斥锁
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
//初始化互斥锁函数
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
//函数介绍:
/********************************************************
函数作用:用于初始化一个互斥锁(restrict代表对象已经被指针所引用,不能通过除该指针外所有其他直接或间接的方式修改该对象的内容)
形参:mutex:我们定义的互斥锁
attr:初始化互斥锁给它的属性(一般可给NULL)
返回值:
成功: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);
//函数介绍:
/********************************************************
函数作用:
pthread_mutex_lock:用于给一个互斥锁上锁,若已被其他线程上锁则阻塞等待
pthread_mutex_trylock:尝试给一个互斥锁上锁;上锁成功返回0,失败直接返回
pthread_mutex_unlock:给一个互斥锁解锁
形参:mutex:我们在前面定义的互斥锁
返回值:
成功:0
失败:相对应的错误码
********************************************************/
//销毁互斥锁函数
int pthread_mutex_destroy(pthread_mutex_t *mutex);
//函数介绍:
/********************************************************
函数作用:用于销毁一个互斥锁
形参:mutex:我们定义的互斥锁
返回值:
成功:0
失败:错误码
********************************************************/
测试代码:
单个互斥锁使用代码:
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
char buf[128];
//定义互斥锁
pthread_mutex_t mutex;
void *func(void *arg)
{
char*buf1=(char*)arg;
for(int i=0;i<10;i++)
{
//上锁
pthread_mutex_lock(&mutex);
//打印一下传递的参数
printf("i am %s\n",buf1);
//解锁
pthread_mutex_unlock(&mutex);
//睡眠1微秒,防止重复抢到锁
usleep(1);
}
}
void *func1(void *arg)
{
char*buf1=(char*)arg;
for(int i=0;i<10;i++)
{
//上锁
pthread_mutex_lock(&mutex);
//打印一下传递的参数
printf("i am %s\n",buf1);
//解锁
pthread_mutex_unlock(&mutex);
//睡眠1微秒,防止重复抢到锁
usleep(1);
}
}
int main(int argc,char **argv)
{
//初始化互斥锁
pthread_mutex_init(&mutex,NULL);
//创建两个线程
pthread_t id1,id2;
pthread_create(&id1,NULL,func,"zhangsan");
pthread_create(&id2,NULL,func1,"lisi");
//睡眠1秒防止互斥锁被提前销毁
sleep(1);
//销毁互斥锁
pthread_mutex_destroy(&mutex);
//回收线程资源
pthread_join(id1,NULL);
pthread_join(id2,NULL);
return 0;
}
运行结果:
当线程创建后,由于线程2先抢到锁因此先打印了lisi,然后因为加了延时,因此间隔输出,否则会因为计算机的运行速度太快先执行完一个线程的再去执行另外一个线程的
多个互斥锁的用法:
/*
测试互斥锁,在写入时不能读取,在读取时不能写入
*/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
//定义两个互斥锁,一个用于写入数据的锁,一个作于读取的锁
pthread_mutex_t mutex;//写入数据的锁
pthread_mutex_t mutex1;//读取的锁
void *func(void *arg)
{
//把arg的类型强转为char *
char *buf = (char *)arg;
while(1)
{
//读取前上锁(可以读取数据)
pthread_mutex_lock(&mutex1);
//打印读取的结果
printf("data:%s\n",buf);
//读取结束后解锁(然后可以从键盘输入数据)
pthread_mutex_unlock(&mutex);
}
}
int main()
{
//初始化互斥锁
pthread_mutex_init(&mutex,NULL);
pthread_mutex_init(&mutex1,NULL);
//先给读取的上一个锁,防止先执行了
//上锁
pthread_mutex_lock(&mutex1);
//定义一个数组,用于从键盘获取数据
char data[20];
bzero(data,20);
//创建一个线程
pthread_t id;
//创建一个线程
pthread_create(&id,NULL,func,data);
//循环输入数据
while(1)
{
//上锁(可以写入数据)
pthread_mutex_lock(&mutex);
//每次输入前清空一下数组
bzero(data,20);
printf("请输入要发送的数据:");
fgets(data,20,stdin);
//解锁(可以读取数据)
pthread_mutex_unlock(&mutex1);
}
//销毁互斥锁
pthread_mutex_destroy(&mutex);
pthread_mutex_destroy(&mutex1);
return 0;
}
运行结果:
当线程创建后,由于先给读取的上了锁,因此不会出现还没开始输入就打印的情况,而是会每次等输入结束后给读取解锁才会去执行读取数据的线程的代码,否则会一直处于挂起状态等待解锁