优先级继承和优先级天花板,均可以解决优先级翻转问题。
优先级翻转:
实例观察优先级翻转和优先级继承现象-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。