本文共 4048 字,大约阅读时间需要 13 分钟。
互斥锁的底层是一个互斥量,而互斥量的本质是一个计数器,计数器的取值只有两种,一种是1,一种是0。1表示当前临界资源可以被访问,0表示当前临界资源不可以被访问。
获取/释放互斥锁的逻辑:
1.调用加锁接口,加锁接口内部判断计数器的值是否为1,如果为1,则能访问,并且如果加锁成功之后,就会将计数器的值从1变为0,如果为0,则不能访问。 2.调用解锁逻辑,计数器的值从0变成1,表示资源可用。加锁:
寄存器和内存当中的计数器的值进行交换,判断寄存器的当中的值是否为1,如果为1,则能够加锁,反之不能。初始化互斥锁变量
互斥锁的类型:pthread_mutex_tpthread_mutex_t mutex = PTHREAD_MUTRX_INITIALIZER
这是一个宏定义,本质上包含多个值
root用户下执行下面语句可以打开查看 vim /user/include/pthread.hvim /usr/include/bits/pthreadtypes.h
pthread_mutex_init函数
int pthread_mutex_init(pthread_mutex_t * mutex, const pthread_mutexattr_t * attr); mutex:该参数为出参,由调用者传递一个互斥锁变量的地址,由pthread_mutex_init这个函数进行初始化 attr:互斥锁的属性信息,一般设置为NULL,采用默认属性 注意: 动态初始化互斥锁变量的情况需要动态销毁互斥锁(pthread_mutex_destroy),否则就会造成内存泄露。 1.阻塞加锁接口:pthread_mutex_lock函数
int pthread_mutex_lock(pthread_mutex_t *mutex); 注意: 如果互斥锁变量当中的计数器的值为1,调用该接口,则加锁成功,该接口调用完毕,函数返回。如果互斥锁变量的那个汇总的计数器的值为0,调用该接口,则调用该接口的执行流阻塞。 2.非阻塞加锁接口:pthread_mutex_trylock函数
int pthread_mutex_trylock(pthread_mutex_t *mutex); 注意: 不管有没有加锁成功,都会返回,所以,需要对加锁结果进行判断,判断是否加锁,如果加锁成功,则操作临界资源,反之,则需要循环获取互斥锁,直到拿到互斥锁。 3.带有超时时间的加锁接口:pthread_mutex_timedlock
int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex, const struct timespec *restrict abs_timeout); 超时时间内,如果没有获得互斥锁,则返回,超时时间内,如果获取了互斥锁也直接返回。
注意: 带有超时时间的加锁接口还需要引入头文件time.h
pthread_mutex_unlock函数
int pthread_mutex_unlock(pthread_t* mutex) 注意: 不管是阻塞加锁/非阻塞加锁/timedlock加锁成功都可以使用该接口进行解锁。 pthread_mutex_destroy
int pthread_mutex_destroy(pthread_t* mutex) 功能: 释放动态开辟的互斥锁的资源 在创建工作线程之前,进行初始化互斥锁。
在执行流访问临界资源之前进行加锁操作。
注意: 一个执行流加锁成功之后,再去获取互斥锁,该执行流也会阻塞。加锁之后一定要记得解锁,否则就会导致死锁。在执行流所有有可能退出的地方进行解锁。
在所有使用该互斥锁的线程全部退出之后,就可以释放该互斥锁了。
#include#include #include #define THREAD_NUM 2int g_tickets = 100000;pthread_mutex_t my_lock;void* MyThreadStart(void* arg){ while(1) { pthread_mutex_lock(&my_lock); if(g_tickets > 0) { printf("i have %d, i am %p\n", g_tickets, pthread_self()); g_tickets--; } else { pthread_mutex_unlock(&my_lock); pthread_exit(NULL); } pthread_mutex_unlock(&my_lock); } return NULL;}int main(){ pthread_mutex_init(&my_lock, NULL); pthread_t tid[THREAD_NUM]; for(int i = 0; i < THREAD_NUM; i++) { int ret = pthread_create(&tid[i], NULL, MyThreadStart, NULL); if(ret < 0) { perror("pthread_create"); return 0; } } for(int i = 0; i < THREAD_NUM; i++) { pthread_join(tid[i], NULL); } pthread_mutex_destroy(&my_lock); printf("phread_join end...\n"); return 0;}

因为我这使用的这个机器只有一个CPU所以,线程只能是并发的执行。 简单的定义:当一个执行流获取到互斥锁之后,并没有进行解锁,就会导致其他执行流由于获取不到锁资源而进行阻塞,我们将这个现象称之为死锁。
复杂的定义:当线程A获取到互斥锁1,线程B获取到互斥锁2的时候,线程A和线程B同时还想获取对方手中的锁(线程A还想获取互斥锁2,线程B还想获取互斥锁1),此时就会导致死锁。
#include#include #include pthread_mutex_t lock_1;pthread_mutex_t lock_2;void* MyThread_A(void* arg){ pthread_mutex_lock(&lock_1); sleep(2); pthread_mutex_lock(&lock_2); return NULL;}void* MyThread_B(void* arg){ pthread_mutex_lock(&lock_2); sleep(2); pthread_mutex_lock(&lock_1); return NULL;}int main(){ pthread_mutex_init(&lock_1, NULL); pthread_mutex_init(&lock_2, NULL); pthread_t tid_A, tid_B; pthread_create(&tid_A, NULL, MyThread_A, NULL); pthread_create(&tid_B, NULL, MyThread_B, NULL); pthread_join(tid_A, NULL); pthread_join(tid_A, NULL); pthread_mutex_destroy(&lock_1); pthread_mutex_destroy(&lock_2); return 0;}
1.执行死锁的代码
2.查看进程号
3.调试正在运行的进程
4.查看当前线程(主线程)的调用堆栈
5.将查看调用堆栈这个功能应用于所有线程
6.跳转到3号线程
7.查看锁的情况
1.不可剥夺:执行流获取了互斥锁之后,除了自己主动释放锁,其他执行流不能解释该互斥锁
2.循环等待 3.互斥条件:一个互斥锁只能被一个执行流在同一时刻拥有 4.请求与保持:“吃着碗里瞧着锅里”1.破坏必要条件:循环等待或者请求与保持
2.加锁顺序一致 3.避免锁没有被释放 4.资源一次性分配1.死锁检测算法
2.银行家算法转载地址:http://soxcz.baihongyu.com/