優先級繼承和優先級天花板,均可以解決優先級翻轉問題。
優先級翻轉:
實例觀察優先級翻轉和優先級繼承現象-CSDN博客
如果有兩個線程A和B,A的優先級大于B的優先級。在B獲取鎖之后,釋放鎖之前,A想要獲取鎖,這個時候,如果B線程沒有被其它線程搶占,正在運行,那么A等待B執行完畢即可,符合預期,既然用戶實現了這樣的業務,即兩個不同優先級的線程會搶一個鎖,那么就要有這樣的預期。而如果這個時候出現了第三個線程C,C的優先級大于B,小于A,那么這個時候C會搶占B的執行,這種現象導致的結果才是不符合預期的。優先級翻轉說的是后者這種現象。
優先級繼承和優先級天花板是解決優先級翻轉的兩種方法。在用戶態使用互斥體pthread_mutex_t時,可以設置參數來指定使用優先級繼承協議還是優先級天花板協議。
優先級繼承:
? pthread_mutexattr_init(&mutex_attr);
? pthread_mutexattr_setprotocol(&mutex_attr, PTHREAD_PRIO_INHERIT);
? pthread_mutex_init(&mutex, &mutex_attr);
在B獲取鎖之后,釋放鎖之前,A想要獲取鎖,這個時候為了讓B盡快執行完,會將A的優先級提升到B的優先級。提升B的優先級,讓B盡快執行完,盡快釋放鎖。如下代碼可以觀察到優先級繼承現象。low現成首先獲取到鎖,之后mid執行,之后high要獲取鎖,此時low的優先級調整到high的優先級。
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif#include <linux/types.h>
#include <sched.h>
#include <stdio.h>
#include <string.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
#include <mutex>#define BIND_CPU_CORE 2pthread_mutex_t mutex;
pthread_mutexattr_t mutex_attr;int set_fifo(int prio) {struct sched_param sp = {.sched_priority = prio};int policy = SCHED_FIFO;return sched_setscheduler(0, policy, &sp);
}int32_t set_affinity() {cpu_set_t cpuset;CPU_ZERO(&cpuset);CPU_SET(BIND_CPU_CORE, &cpuset);if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) {printf("bind cpu error\n");return -1;}return 0;
}void *fifo_low(void *data) {pthread_setname_np(pthread_self(), "low");set_fifo(5);set_affinity();printf("fifo low\n");sleep(1);printf("fifo low, before lock\n");pthread_mutex_lock(&mutex);int count = 0;for (int i = 0; i < 60; i++) {count++;printf("low count %d\n", count);sleep(1);}printf("fifo low, after lock\n");while (1);
}void *fifo_mid(void *data) {pthread_setname_np(pthread_self(), "mid");set_fifo(10);set_affinity();printf("fifo mid\n");sleep(1);while (1);
}void *fifo_high(void *data) {pthread_setname_np(pthread_self(), "high");set_fifo(15);set_affinity();printf("fifo high\n");sleep(1);printf("fifo high, before lock\n");pthread_mutex_lock(&mutex);printf("fifo high, after lock\n");while (1);
}int main() {pthread_t fifo_tid1;pthread_t fifo_tid2;pthread_t fifo_tid3;pthread_mutexattr_init(&mutex_attr);pthread_mutexattr_setprotocol(&mutex_attr, PTHREAD_PRIO_INHERIT);pthread_mutex_init(&mutex, &mutex_attr);sleep(5);pthread_create(&fifo_tid1, NULL, fifo_low, NULL);sleep(5);pthread_create(&fifo_tid2, NULL, fifo_mid, NULL);sleep(30);pthread_create(&fifo_tid3, NULL, fifo_high, NULL);sleep(1000);return 0;
}
優先級繼承適用于實時線程之間,以及實時線程和普通線程之間,不適用于普通線程之間。線程設置為了普通調度策略,那么說明對實時性沒有要求,那么也沒有必要進行優先級繼承。
優先級天花板:
假如用戶知道使用鎖的線程的最高優先級是30,那么可以設置優先級天花板是30。那么不管哪個線程獲取到鎖,或者在等待鎖,那么線程的優先級都是調整為30。
pthread_mutex_t 默認支持優先級繼承,std::mutex不支持優先級繼承:
在linux下pthread_mutex_t,如果不通過pthread_mutexattr_setprotocol設置協議,那么默認為優先級繼承協議。c++中的std::mutex不支持優先級繼承,所以在使用c++開發應用時,如果對實時性有要求,那么可以使用pthred_mutex_t。