💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
[TOC] # 简介 而在线程里也有这么一把锁:互斥锁(mutex),也叫互斥量,互斥锁是一种简单的加锁的方法来控制对共享资源的访问,互斥锁只有两种状态,即**加锁**( lock )和**解锁**( unlock )。 互斥锁的操作流程如下: 1)在访问共享资源后临界区域前,对互斥锁进行加锁。 2)在访问完成后释放互斥锁导上的锁。 3)对互斥锁进行加锁后,任何其他试图再次对互斥锁加锁的线程将会被阻塞,直到锁被释放。 互斥锁的数据类型是: pthread\_mutex\_t。 安装对应帮助手册: > $ sudo apt-get install -y manpages-posix-dev # pthread\_mutex\_init函数 初始化互斥锁: ~~~ #include <pthread.h> ​ int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr); 功能: 初始化一个互斥锁。 参数: mutex:互斥锁地址。类型是 pthread_mutex_t 。 attr:设置互斥量的属性,通常可采用默认属性,即可将 attr 设为 NULL。 ​ 可以使用宏 PTHREAD_MUTEX_INITIALIZER 静态初始化互斥锁,比如: pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; ​ 这种方法等价于使用 NULL 指定的 attr 参数调用 pthread_mutex_init() 来完成动态初始化,不同之处在于 PTHREAD_MUTEX_INITIALIZER 宏不进行错误检查。 ​ 返回值: 成功:0,成功申请的锁默认是打开的。 失败:非 0 错误码 ~~~ # pthread\_mutex\_destroy函数 ~~~ #include <pthread.h> ​ int pthread_mutex_destroy(pthread_mutex_t *mutex); 功能: 销毁指定的一个互斥锁。互斥锁在使用完毕后,必须要对互斥锁进行销毁,以释放资源。 参数: mutex:互斥锁地址。 返回值: 成功:0 失败:非 0 错误码 ~~~ ​ # pthread_mutex_lock函数 ~~~ #include <pthread.h> ​ int pthread_mutex_lock(pthread_mutex_t *mutex); 功能: 对互斥锁上锁,若互斥锁已经上锁,则调用者阻塞,直到互斥锁解锁后再上锁。 参数: mutex:互斥锁地址。 返回值: 成功:0 失败:非 0 错误码 ​ int pthread_mutex_trylock(pthread_mutex_t *mutex); * 调用该函数时,若互斥锁未加锁,则上锁,返回 0; * 若互斥锁已加锁,则函数直接返回失败,即 EBUSY。 ~~~ # pthread_mutex_unlock函数 ~~~ #include <pthread.h> ​ int pthread_mutex_unlock(pthread_mutex_t *mutex); 功能: 对指定的互斥锁解锁。 参数: mutex:互斥锁地址。 返回值: 成功:0 失败:非0错误码 ​~~~ # 例子 ​~~~ pthread_mutex_t mutex; //互斥锁 // 打印机 void printer(char *str) { pthread_mutex_lock(&mutex); //上锁 while (*str != '\0') { putchar(*str); fflush(stdout); str++; sleep(1); } printf("\n"); pthread_mutex_unlock(&mutex); //解锁 } ​ // 线程一 void *thread_fun_1(void *arg) { char *str = "hello"; printer(str); //打印 } ​ // 线程二 void *thread_fun_2(void *arg) { char *str = "world"; printer(str); //打印 } ​ int main(void) { pthread_t tid1, tid2; ​ pthread_mutex_init(&mutex, NULL); //初始化互斥锁 ​ // 创建 2 个线程 pthread_create(&tid1, NULL, thread_fun_1, NULL); pthread_create(&tid2, NULL, thread_fun_2, NULL); ​ // 等待线程结束,回收其资源 pthread_join(tid1, NULL); pthread_join(tid2, NULL); ​ pthread_mutex_destroy(&mutex); //销毁互斥锁 ​ return 0; } ~~~ # 死锁 **死锁引起的原因** 竞争不可抢占资源引起死锁 也就是我们说的第一种情况,而这都在等待对方占有的不可抢占的资源。 竞争可消耗资源引起死锁 有p1,p2,p3三个进程,p1向p2发送消息并接受p3发送的消息,p2向p3发送消息并接受p1的消息,p3向p1发送消息并接受p2的消息,如果设置是先接到消息后发送消息,则所有的消息都不能发送,这就造成死锁。 进程推进顺序不当引起死锁 有进程p1,p2,都需要资源A,B,本来可以p1运行A --> p1运行B --> p2运行A --> p2运行B,但是顺序换了,p1运行A时p2运行B,容易发生第一种死锁。互相抢占资源。 **3)死锁的必要条件** 互斥条件 某资源只能被一个进程使用,其他进程请求该资源时,只能等待,直到资源使用完毕后释放资源。 请求和保持条件 程序已经保持了至少一个资源,但是又提出了新要求,而这个资源被其他进程占用,自己占用资源却保持不放。 不可抢占条件 进程已获得的资源没有使用完,不能被抢占。 循环等待条件 必然存在一个循环链。 **4)处理死锁的思路** 预防死锁 破坏死锁的四个必要条件中的一个或多个来预防死锁。 避免死锁 和预防死锁的区别就是,在资源动态分配过程中,用某种方式防止系统进入不安全的状态。 检测死锁 运行时出现死锁,能及时发现死锁,把程序解脱出来 解除死锁 发生死锁后,解脱进程,通常撤销进程,回收资源,再分配给正处于阻塞状态的进程。 **5)预防死锁的方法** ***破坏请求和保持条件*** 协议1: 所有进程开始前,必须一次性地申请所需的所有资源,这样运行期间就不会再提出资源要求,破坏了请求条件,即使有一种资源不能满足需求,也不会给它分配正在空闲的资源,这样它就没有资源,就破坏了保持条件,从而预防死锁的发生。 协议2: 允许一个进程只获得初期的资源就开始运行,然后再把运行完的资源释放出来。然后再请求新的资源。 ***破坏不可抢占条件*** 当一个已经保持了某种不可抢占资源的进程,提出新资源请求不能被满足时,它必须释放已经保持的所有资源,以后需要时再重新申请。 ***破坏循环等待条件*** 对系统中的所有资源类型进行线性排序,然后规定每个进程必须按序列号递增的顺序请求资源。假如进程请求到了一些序列号较高的资源,然后有请求一个序列较低的资源时,必须先释放相同和更高序号的资源后才能申请低序号的资源。多个同类资源必须一起请求。