在多线程应用程序中,有多个线程同时运行。如果多个线程试图同时访问同一段代码(通常称为临界区) ,则可能导致意外或错误的行为。这就是需要线程同步的地方;这是一种确保在任何给定时间只有一个线程执行临界区的机制。
您可以使用两种机制来实现线程同步,即信号量或互斥锁。它们具有一些不同的属性,但本质上它们都是由线程在关键部分之前和之后更改的变量,以确保在该线程完成之前没有其他线程可以执行该段。主要区别在于信号量具有在初始化时设置的最大值,而互斥锁具有所有权属性,即只有增加其值的线程才能减少它,直到放弃时为零。
Semaphores(信号量)
信号量最简单的形式就是一个可以改变的普通变量,用于指示公共资源的状态。信号量可以看作是一种资源共享机制,其中您拥有一个有限的资源实例,并且需要管理多个线程对该资源的访问。它们更像是一种信号机制,用于控制对给定数量的资源实例的访问。
信号量具有以下属性:
- 在初始化时,您设置一个初始计数(大于 0)和一个最大限制。
- “Give” 将增加信号量计数,除非计数已达到最大限制,在这种情况下信号将不会增加。“Give” 可以从任何线程或 ISR 中完成。
- “Take” 将减少信号量计数,除非信号量不可用(计数为零)。任何试图获取不可用信号量的线程都需要等待,直到其他线程使其可用(通过提供信号量)。“Take” 只能在线程中完成,而不能在 ISR 中完成(因为 ISR 不应该阻塞任何事情)。
- 信号量没有所有权。这意味着一个信号量可以由一个线程获取,也可以由任何线程提供。获取信号量的线程不一定是提供信号的线程。
- 获取信号量的线程不符合优先级继承条件,因为获取信号量的线程并不拥有该信号量,并且任何其他线程都可以提供该信号量。
下图显示了信号量的属性
Mutexes(互斥锁)
与信号量相反,互斥锁只能采用两个值,通常称为已锁定或已解锁。此外,互斥锁具有所有权属性,即只有锁定互斥锁的线程才能将其解锁。可以将其视为具有单个密钥的锁定/解锁机制,其中希望访问单个对象的线程。例如,代码段或资源需要首先访问未锁定的互斥锁,将其锁定,然后访问对象。如果尝试访问互斥锁的线程发现互斥锁已被锁定,则该线程将被阻止并等待,直到锁定它的线程解锁互斥锁。
互斥锁的典型用途是保护可从多个线程访问的代码的关键部分。关键部分是一段需要在不受其他线程干扰的情况下完成的代码,否则该关键部分内的全局/静态数据可能会被误传或损坏。
互斥锁具有以下属性:
- 锁定互斥锁将增加锁定计数。递归锁定(可重入锁定)不会使锁定线程阻塞,因为它已经拥有互斥锁。线程应确保解锁互斥锁的次数与锁定互斥锁的次数相同,以便释放互斥锁,以便其他线程可以尝试拥有它。
- 解锁互斥锁将减少锁定计数。当锁定计数为零时,这意味着互斥锁处于解锁状态。只有当互斥锁处于解锁状态时,线程才能尝试拥有该互斥锁。
- 只有锁定互斥锁的线程才能将其解锁。
- 互斥锁的锁定和解锁只能在线程中完成,而不能在 ISR 中完成。这是因为 ISR 不能参与调度程序的所有权和优先级继承机制。
- 锁定互斥锁的线程有资格获得优先级继承,因为只有该线程可以解锁互斥锁。
下图显示了互斥锁的属性