Я использую условные переменные SCHED_FIFO и pthread.
Программа состоит из 3 потоков: «основной» с более высоким приоритетом для процессора 0, «задача_1» также для процессора 0 и «задача_2» ссамый низкий приоритет на процессоре 1.
Для точки зрения переменной условия posix «main» - это производитель, а остальные потоки - «потребители».
Проблема заключается в следующем: после вызова широковещания основной прерывается задачей task_1, которая имеет более низкий приоритет, а затем task_2 также завершает свое выполнение до основной ...
Трасса (всегда) следующая:
[main] creating task_2
[task_2] lock
[task_2] wait
[main] creating task_1
[task_1] lock
[task_1] wait
[main] before lock
[main] before unlock
[task_1] running
[main] after unlock
[task_1] unlock
[task_2] running
[main] finished
[task_2] unlock
using time_ns_t = uint64_t;
constexpr time_ns_t sec_to_us(time_ns_t sec) {
return sec * 1000000;
}
pthread_mutex_t mutex;
void mutex_init() {
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
// pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT);
pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_NONE);
pthread_mutex_init(&mutex, &attr);
}
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
bool ready = false;
//--------------------------------------------------
uint64_t total_usage(const struct rusage& usage) {
uint64_t u_sec = usage.ru_utime.tv_sec;
uint64_t u_micro = usage.ru_utime.tv_usec;
uint64_t s_sec = usage.ru_stime.tv_sec;
uint64_t s_micro = usage.ru_stime.tv_usec;
return (u_sec + s_sec) * 1000000 + (u_micro + s_micro);
}
uint64_t burn_cpu(uint64_t microsec) {
struct rusage usage;
getrusage(RUSAGE_THREAD, &usage);
uint64_t init = total_usage(usage);
uint64_t duration = 0;
while (duration < microsec) {
getrusage(RUSAGE_THREAD, &usage);
duration = total_usage(usage) - init;
}
return duration;
}
//--------------------------------------------------
int set_cpu(int cpu) {
cpu_set_t set;
CPU_ZERO(&set);
CPU_SET(cpu, &set);
return sched_setaffinity(0, sizeof(cpu_set_t), &set);
}
int set_rt_priority(int priority) {
struct sched_param sp;
sp.sched_priority = priority;
return sched_setscheduler(0, SCHED_FIFO, &sp);
}
//--------------------------------------------------
void* thread_start_1(void* data) {
set_cpu(0);
set_rt_priority(20);
std::cout << "[task_1] lock\n";
pthread_mutex_lock(&mutex);
while (!ready) {
std::cout << "[task_1] wait\n";
pthread_cond_wait(&cond, &mutex);
}
std::cout << "[task_1] running\n";
burn_cpu(sec_to_us(1));
pthread_mutex_unlock(&mutex);
std::cout << "[task_1] unlock\n";
return nullptr;
}
void* thread_start_2(void* data) {
set_cpu(1);
set_rt_priority(10);
std::cout << "[task_2] lock\n";
pthread_mutex_lock(&mutex);
while (!ready) {
std::cout << "[task_2] wait\n";
pthread_cond_wait(&cond, &mutex);
}
std::cout << "[task_2] running\n";
burn_cpu(sec_to_us(1));
pthread_mutex_unlock(&mutex);
std::cout << "[task_2] unlock\n";
return nullptr;
}
int main(int argc, char* argv[]) {
pthread_t id_1, id_2;
set_cpu(0);
set_rt_priority(50);
burn_cpu(sec_to_us(1));
std::cout << "[main] creating task_2\n";
pthread_create(&id_2, nullptr, thread_start_2, nullptr);
burn_cpu(sec_to_us(1));
std::cout << "[main] creating task_1\n";
pthread_create(&id_1, nullptr, thread_start_1, nullptr);
burn_cpu(sec_to_us(1));
std::cout << "[main] before lock\n";
pthread_mutex_lock(&mutex);
ready = true;
pthread_cond_broadcast(&cond);
std::cout << "[main] before unlock\n";
pthread_mutex_unlock(&mutex);
std::cout << "[main] after unlock\n";
burn_cpu(sec_to_us(1));
std::cout << "[main] finished\n";
pthread_join(id_1, nullptr);
pthread_join(id_2, nullptr);
return 0;
}