ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

自学嵌入式Day29:Linux多线程控制(互斥锁、信号量与死锁)

自学嵌入式Day29:Linux多线程控制(互斥锁、信号量与死锁)

目录

一、线程互斥(Mutex)

1. 核心概念

2. 互斥锁的使用步骤

3. 相关 API 函数

4. 互斥锁的注意事项

二、线程同步(Semaphore)

1. 核心概念

2. 互斥锁 vs 信号量(重点区分)

3. 信号量的使用步骤

4. 相关函数

三、死锁(Deadlock)

1. 什么是死锁?

2. 产生死锁的四个必要条件(缺一不可)


一、线程互斥(Mutex)

1. 核心概念

  • 临界资源:在多线程中,会被多个线程同时进行读写操作的资源(如全局变量、文件、硬件设备等)。

  • 互斥(Mutual Exclusion):在多线程中,对临界资源的排他性访问。即同一时刻,只能有一个线程访问该资源。

2. 互斥锁的使用步骤

定义锁 →→ 初始化锁 →→ 加锁 →→ 解锁 →→ 销毁锁

3. 相关 API 函数

使用互斥锁需包含头文件<pthread.h>。

  1. 定义与初始化
    pthread_mutex_t mutex; // 定义互斥锁 int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr); // 功能:初始化互斥锁。 // 参数:attr 一般传 NULL,表示默认属性。 // 返回值:成功 0,失败 非零。
  2. 加锁
    int pthread_mutex_lock(pthread_mutex_t *mutex); // 功能:加锁。加锁后的代码到解锁部分的代码属于“原子操作”。 // 如果 mutex 已经被其他线程锁住,当前线程会在这里阻塞等待。

    原子操作:在线程的一次调度中,这段代码必须完成,不能中途发生线程调度。

  3. 解锁
    int pthread_mutex_unlock(pthread_mutex_t *mutex); // 功能:解锁。解锁后代码不再排他访问。
  4. 销毁
    int pthread_mutex_destroy(pthread_mutex_t *mutex); // 功能:使用完毕后销毁锁,释放资源。

4. 互斥锁的注意事项

  1. 谁加锁,谁解锁:互斥锁的加锁和解锁必须在同一个线程中进行。
  2. 临界区要“短小精悍”:在被互斥锁保护的代码段(临界区)内,绝对不要执行休眠(sleep)或耗时极长的操作,否则会导致其他线程长时间阻塞,严重影响并发效率。

二、线程同步(Semaphore)

1. 核心概念

  • 同步:有一定先后顺序的对资源的排他性访问。
  • 关系:互斥包含同步,同步是互斥的一个特例。

2. 互斥锁 vs 信号量(重点区分)

  1. 持有者:互斥锁有持有者的概念(谁加锁谁解锁);信号量没有持有者,可以由线程 A 释放、线程 B 申请(th1 释放 th2,th2 释放 th1,交叉释放)。
  2. 临界区限制:互斥锁保护的临界区不能休眠;而信号量保护的区域适当可以有休眠或小的耗时操作。
  3. 资源数:互斥锁资源唯一;信号量是计数信号量,其初值可以大于 1(适用于有多个相同资源的情况)。

3. 信号量的使用步骤

定义信号量 →→ 初始化 →→ P/V 操作 →→ 销毁。

4. 相关函数

使用信号量需包含头文件<semaphore.h>。

  • 定义与初始化
    sem_t sem; // 定义信号量 int sem_init(sem_t *sem, int pshared, unsigned int value); // 功能:初始化信号量。 // 参数:pshared == 0 表示线程间使用;!=0 表示进程间使用。 // value 是初始值。二值信号量(0 或 1),0表示红灯阻塞,1表示绿灯通行。
  • P/V 操作
    • P 操作(申请资源):对应sem_wait()。如果有资源,申请并执行sem = sem - 1;如果没有资源,阻塞等待。
      int sem_wait(sem_t *sem);
    • V 操作(释放资源):对应sem_post()。释放资源并执行sem = sem + 1,不会阻塞。
      int sem_post(sem_t *sem);
  • 销毁
    int sem_destroy(sem_t *sem);

三、死锁(Deadlock)

1. 什么是死锁?

由于锁资源安排不合理(申请和释放逻辑不对),导致多个进程或线程互相等待对方释放资源,从而无法继续执行(推进)的现象。

2. 产生死锁的四个必要条件(缺一不可)

这是计算机科学中非常经典的考点:

  1. 互斥条件:一个资源每次只能被一个进程使用。
  2. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
  3. 不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺。
  4. 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

破局之法:我们在写代码时,只要破坏上述四个条件中的任意一个(例如:规定所有线程按相同的顺序申请锁),就可以避免死锁。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进