1、什么是互斥锁
如果信号量的值最多为 1,那实际上相当于一个共享资源在任意时刻最多只能有一个线程在访问,这样的逻辑被称为“互斥”。这时,有一种更加方便和语义更加准确的工具来满足这种逻辑,他就是互斥锁。
“锁”是一种非常形象的说法:就像一个房间只能住一个人一样,任何人进去之后就把门锁上了,其他任何人都不能进去,直到进去的那个人重新开开锁,即释放了这个锁资源为止。
互斥锁用于保护线程中某一个共享资源, 该资源在任意一个时刻都只允许有一个线程可以访问的情况。互斥锁是专门用于处理线程互斥的一种方式,它有两种状态:上锁状态、解锁状态。
例如有多个线程有可能会对某一个链表进行删除、插入的操作。那么任何一个线程想要操作该链表前都应该使用互斥锁来阻塞等待操作的权限(pthread_mutex_lock),当获得权限后上锁,阻止其他线程来同时访问该链表造成链表的错乱。当操作结束后使用解锁函数(pthread_mutex_unlock)把锁资源释放出来。
特点: 如果互斥锁处于上锁状态,那么再上锁就会造成阻塞,直到这把锁解开了之后,才能上锁。解锁状态依然继续解锁,不会阻塞。
2、线程互斥锁API接口
(1)、定义互斥锁变量
数据类型: pthread_mutex_t
pthread_mutex_t m;
(2)、初始化互斥锁
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *attr);参数:mutex: 未初始化过互斥锁变量的地址mutexattr:普通属性,NULL
返回值:成功:0失败:非0错误码
静态初始化:
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
也就是说,以上这句话等价于:
pthread_mutex_t m;
pthread_mutex_init(&m,NULL);
(3)、线程上锁
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
参数:mutex:互斥锁变量的地址
返回值:成功:0失败:非0错误码
(4)、线程解锁
#include <pthread.h>
int pthread_mutex_unlock(pthread_mutex_t *mutex);
参数:mutex:互斥锁变量的地址
返回值:成功:0失败:非0错误码
(5)、销毁互斥锁
#include <pthread.h>
int pthread_mutex_destroy(pthread_mutex_t *mutex);
参数:mutex:互斥锁变量的地址
返回值:成功:0失败:非0错误码
3、互斥锁应用实例
互斥锁使用场景:当我们使用一些临界资源时,防止多个线程同时访问,我们可以这么做,在访问临界资源前,让线程先上锁,然后再访问资源,访问完了之后就解锁,让别的线程去上锁。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>int g_val = 0;
//1)定义互斥锁变量。 -----》数据类型 pthread_mutex_t
pthread_mutex_t mutex;//线程的例程函数,也就是创建线程之后,去执行这个函数
void* routine1(void *arg)
{pthread_mutex_lock(&mutex);//上锁//写操作,修改内存空间的值 g_val = 100;int i;for(i=0; i<5; i++){sleep(1); g_val += g_val*i;printf("%d routine1 100 g_val:%d\n",i,g_val); }pthread_mutex_unlock(&mutex);//解锁
}//线程的例程函数,也就是创建线程之后,去执行这个函数
void* routine2(void *arg)
{pthread_mutex_lock(&mutex);//上锁//写操作,修改内存空间的值 g_val = 200;int i;for(i=0; i<5; i++){sleep(1); g_val += g_val*i; printf("routine2 200 g_val:%d\n",g_val); }pthread_mutex_unlock(&mutex);//解锁
}//线程的例程函数,也就是创建线程之后,去执行这个函数
void* routine3(void *arg)
{pthread_mutex_lock(&mutex);//上锁int i;//读操作,此时仅仅只是将这个值打印出来for(i=0; i<5; i++){sleep(1); printf("routine3 g_val:%d\n",g_val); }pthread_mutex_unlock(&mutex);//解锁
}//线程的例程函数,也就是创建线程之后,去执行这个函数
void* routine4(void *arg)
{pthread_mutex_lock(&mutex);//上锁int i;//读操作,此时仅仅只是将这个值打印出来for(i=0; i<5; i++){sleep(1); printf("routine4 g_val:%d\n",g_val); }pthread_mutex_unlock(&mutex);//解锁
}int main(int argc, char **argv)
{//2)初始化 互斥锁pthread_mutex_init(&mutex,NULL);// 创建一个新的线程1pthread_t thread1; pthread_create(&thread1,NULL,routine1,NULL); // 创建一个新的线程2pthread_t thread2; pthread_create(&thread2,NULL,routine2,NULL); // 创建一个新的线程3pthread_t thread3; pthread_create(&thread3,NULL,routine3,NULL); // 创建一个新的线程4pthread_t thread4; pthread_create(&thread4,NULL,routine4,NULL); //接合子线程 --阻塞等待子线程退出 回收资源pthread_join(thread1,NULL);pthread_join(thread2,NULL);pthread_join(thread3,NULL);pthread_join(thread4,NULL);//5)销毁互斥锁pthread_mutex_destroy(&mutex);return 0;
}
如下图所示为互斥锁程序的演示结果。从终端打印输出的信息,可以看到每个线程都是依次有序的排队进行获取释放互斥锁进行运行的。