zephyr线程同步
概述
在多线程应用程序中,当多个线程同时运行时,便产生了线程同步的需求。本课程将阐述线程同步的必要性,并讲解如何使用信号量和互斥锁作为线程同步机制。
在练习部分,我们将重点介绍两种常见的线程同步问题,并展示如何使用信号量和互斥锁来解决这些问题。
目标
- 理解线程同步机制的需求
- 学习信号量和互斥锁的基本属性
- 通过动手练习,掌握如何使用信号量和互斥锁进行线程同步
线程同步
在多线程应用程序中,存在多个线程同时运行的情况。如果超过一个线程试图同时访问同一资源(变量、数据结构等),通常这被称之为关键区段,这可能会导致出现意外或错误的行为。这正是需要实现线程同步的地方;它是一种机制,旨在确保在任何给定时刻只有一条线程执行关键区段
实现线程同步的两个机制是信号量或互斥锁。它们具有一些不同的特性,但本质上都属于在临界区前后由线程进行更改的变量,以确保其他线程无法先于该线程完成该段代码的执行。主要区别在于信号量拥有一个在初始化时设定的最大值,而互斥锁则具有所有权属性,即只有增加其值的线程才能减少其值,直至降至零时将其释放。
在接下来的章节中,我们将更详细地探讨信号量和互斥锁这两种机制,包括其属性列表及可视化展示。此外,还需完成两项练习,以实践如何将信号量和互斥锁作为三种同步机制加以运用。
信号量
最简单的形式下,信号量仅是一个普通的变量,它会发生变化,用以指示共享资源的当前状态。信号量可以被视作一种资源共享机制,其中你拥有一种有限的资源实例,而你需要管理该资源对多个线程的访问。它更多地是一种用于控制对特定数量资源实例访问的信号机制。
信号量具有以下特性:
- 在初始化时,您设置一个初始计数(大于或等于0)和一个最大限制。
- “Give”将递增信号量计数,除非计数已达到最大限制,在这种情况下信号不会递增。Givee”可以在任何线程或ISR中执行。
-“Take” 操作会减少信号量计数,除非该信号量不可用(计数值为零)。任何试图获取一个不可用信号量的线程都需要等待,直至有其他线程使其变为可用(即释放该信号量)。“Take” 操作只能在线程中进行,而不可在中断服务例程(ISR)中执行(尽管内核确实允许ISR获取信号量,但ISR不得尝试在信号量不可用的情况下进行等待) - 获取信号量的线程不具备优先级继承资格,因为获取线程并不拥有该信号量,而其他任何线程都可以释放该信号量。
下图显示了信号旗的属性
互斥锁
与信号量不同,互斥锁只能处于两种状态,通常被称为“锁定”或“解锁”。此外互斥锁具有所有权属性,即只有锁定该互斥锁的线程才能将其解锁。可以将其视为一种带有单一钥匙的锁定/解锁机制,其中线程希望获取对某个对象的访问权。例如,一段代码或一项资源需要首先获取对未锁定互斥锁的访问权,然后锁定它,接着才能访问该对象。如果尝试获取互斥锁的线程发现该互斥锁已被锁定,那么该线程会被阻塞,并需等待锁定该互斥锁的线程将其解锁。
互斥锁的一种典型用途是保护代码中的关键部分,该部分可由多个线程访问。关键部分是一段代码,它需要在不受其他线程干扰的情况下完成,否则该关键部分内的全局/静态数据可能会遭到误读或损坏。
互斥锁具有以下属性:
- 锁定一个互斥锁会递增锁定计数。递归锁定(可重入锁定)并不会使进行锁定的线程陷入阻塞状态,因为该线程已经持有了互斥锁。线程应确保它解锁互斥锁的次数与锁定时的次数相同,从而释放互斥锁,以便其他线程能够尝试获取它。
- 解锁一个互斥锁会减少锁定计数。当锁定计数为零时,意味着该互斥锁处于未锁定状态。只有当互斥锁处于未锁定状态时,线程才能尝试获取其所有权。
- 解锁一个互斥锁会减少锁定计数。当锁定计数为零时,意味着该互斥锁处于未锁定状态。只有当互斥锁处于未锁定状态时,线程才能尝试获取其所有权。
- 只有锁定互斥锁的线程才能解锁它。
- 互斥锁的锁定和解锁只能在线程中进行,而不能在ISR中执行。这是因为ISR无法参与调度器的所有权和优先级继承机制。
- 锁定互斥锁的线程有资格继承优先级,因为只有该线程才能解锁互斥锁。
