如何使 pthread 任务根据其优先级以正确的顺序执行?

How can I make the pthread tasks executes in the correct order according to their priorities?

我正在尝试创建具有不同优先级的 3 线程任务。该任务只会等待或通知其他线程任务,并将执行保存在 char 数组中以查看它们是否按预期执行。

这是代码

#include <stdio.h>
#include <unistd.h>
#include <stdbool.h>
#include <time.h>
#include <pthread.h>

static void *pT1();
static void *pT2();
static void *pT3();

static void trigT3();
static void trigT2();

pthread_cond_t          gCondVar1;
pthread_cond_t          gCondVar2;
pthread_cond_t          gCondVar3;
pthread_cond_t          gFinish;
pthread_mutex_t         gLock1;
pthread_mutex_t         gLock2;
pthread_mutex_t         gLock3;
pthread_mutex_t         gLockF;

static char             savedExe[4];
static int              gPos = 0;
static bool             gSignalT2 = false;
static bool             gSignalT3 = false;

int main(int argc, char const *argv[])
{

    struct sched_param  param1;
    struct sched_param  param2;
    struct sched_param  param3;

    pthread_attr_t      attr1;
    pthread_attr_t      attr2;
    pthread_attr_t      attr3;

    pthread_t           tid1;
    pthread_t           tid2;
    pthread_t           tid3;

    pthread_attr_init(&attr1);
    pthread_attr_init(&attr2);
    pthread_attr_init(&attr3);

    pthread_attr_setschedpolicy(&attr1, SCHED_FIFO);
    pthread_attr_setschedpolicy(&attr2, SCHED_FIFO);
    pthread_attr_setschedpolicy(&attr3, SCHED_FIFO);

    param1.sched_priority = 10;
    param2.sched_priority = 20;
    param3.sched_priority = 30;

    pthread_attr_setschedparam(&attr1, &param1);
    pthread_attr_setschedparam(&attr2, &param2);
    pthread_attr_setschedparam(&attr3, &param3);

    pthread_create(&tid1, &attr1, pT1, NULL);
    pthread_create(&tid2, &attr2, pT2, NULL);
    pthread_create(&tid3, &attr3, pT3, NULL);

    for (int i = 0; i < sizeof(savedExe); ++i) {
        printf("%c, ", savedExe[i]);
    }
    printf("\b\b\n");

    pthread_cond_signal(&gCondVar1);

    /*.
    .
    .
    .*/
    pthread_cond_wait(&gFinish, &gLockF);

    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    pthread_join(tid3, NULL);

    for (int i = 0; i < sizeof(savedExe); ++i) {
        printf("%c, ", savedExe[i]);
    }
    printf("\b\b\n");

    return 0;
}

static void trigT3(){
    pthread_mutex_lock(&gLock3);
    pthread_cond_signal(&gCondVar3);
    gSignalT3 = true;
    pthread_mutex_unlock(&gLock3);
}

static void trigT2(){
    pthread_mutex_lock(&gLock2);
    pthread_cond_signal(&gCondVar2);
    gSignalT2 = true;
    pthread_mutex_unlock(&gLock2);
}

static void *pT1(){
    pthread_mutex_lock(&gLock1);
    pthread_cond_wait(&gCondVar1, &gLock1);
    trigT3();
    gPos++;
    savedExe[gPos] = '1';
    pthread_mutex_unlock(&gLock1);

    pthread_cond_signal(&gFinish);

    return NULL;
}

static void *pT3(){
    //printf("T3\n");
    pthread_mutex_lock(&gLock3);
    if(!gSignalT3){
        pthread_cond_wait(&gCondVar3, &gLock3);
        gSignalT3 = false;
    }
    trigT2();
    gPos++;
    savedExe[gPos] = '3';
    pthread_mutex_unlock(&gLock3);
    return NULL;
}

static void *pT2(){
    pthread_mutex_lock(&gLock2);
    if(!gSignalT2){
        pthread_cond_wait(&gCondVar2, &gLock2);
        gSignalT3 = false;
    }
    gPos++;
    savedExe[gPos] = '2';
    pthread_mutex_unlock(&gLock2);
    return NULL;
}

总的来说,我创建了 3 个不同的任务并为它们分配了不同的优先级。 pT1 具有最低优先级 = 10,pT2 = 20 和 pT3 = 30。 pT1 首先开始执行,因为它的优先级较低并且它触发了 pT3,pT3 将开始执行。 pT3 将触发 pT2,但由于 pT2 的优先级较低,pT3 将完成执行,然后触发 pT2。

这个输出应该是 3, 2, 1。 但是我得到的是1、2、3,好像它根本不关心任何优先级,只是按照给定的顺序执行任务。

优先级会影响当多个线程想要同时使用 CPU 时线程获得多少 CPU 时间。与这里无关。


问题是你打电话给trig太早了!您希望 T2 在 T3 之后 运行,但是您在 T3 执行操作之前触发了 T2。两者最终都试图同时修改 savedExegPos,这很糟糕。这两个问题都可以通过正确排序语句来避免。

static void *pT3() {
    wait_for_trigger(&gLock3, &gCondVar3, &gSignalT3);
    savedExe[gPos++] = '3';
    trigger(&gLock2, &gCondVar2, &gSignalT2);
    return NULL;
}

Demo 在编译器资源管理器上


请注意,您不需要那么多互斥量和转换变量。您可以对所有信号使用单个互斥锁和条件变量。

static void wait_for_trigger(bool *flag) {
   pthread_mutex_lock(&mutex);
   while (!*flag)
      pthread_cond_wait(&cond, &mutex);
   pthread_mutex_unlock(&mutex);
}

static void trigger(bool *flag) {
   pthread_mutex_lock(&mutex);
   *flag = true;
   pthread_mutex_unlock(&mutex);
   pthread_cond_broadcast(&cond);
}

Demo 在编译器资源管理器上

您甚至可以为不同的优先级使用单个变量!

static void wait_for_level(int a_prio_lvl) {
   pthread_mutex_lock(&mutex);
   while (prio_lvl > a_prio_lvl)
      pthread_cond_wait(&cond, &mutex);
   pthread_mutex_unlock(&mutex);
}

static void switch_to_level(int a_prio_lvl) {
   pthread_mutex_lock(&mutex);
   prio_lvl = a_prio_lvl;
   pthread_mutex_unlock(&mutex);
   pthread_cond_broadcast(&cond);
}

Demo 在编译器资源管理器上


您还有下面提到的许多其他问题。这些都在这个demo中修复了。

原型不正确

static void *pT1();

应该是

static void *pT1(void);

声明中的空括号并不意味着没有参数。您需要使用 void 来表示。

参数不当

static void *pT1() { ... }

应该是

static void *pT1(void *arg) { ... }

pthread_create 使用 void * 参数调用函数。

您可以使用 (void)arg 来消除未使用的参数警告。

未初始化的互斥量

pthread_mutex_t gLock1;

应该是

pthread_mutex_t gLock1 = PTHREAD_MUTEX_INITIALIZER;

互斥锁在使用前需要先初始化!

未初始化的条件变量

pthread_cond_t gCondVar1;

应该是

pthread_cond_t gCondVar1 = PTHREAD_COND_INITIALIZER;

条件变量在使用前需要初始化!

pthread_cond_wait

的不正确期望
if (!gSignalT3){
   pthread_cond_wait(&gCondVar3, &gLock3);
}

应该是

while (!gSignalT3){
   pthread_cond_wait(&gCondVar3, &gLock3);
}

pthread_cond_wait可以return随时。需要明确的是,即使未使用 pthread_cond_signalpthread_cond_wait 也可以 return。因此必须始终在检查某些外部条件的循环中调用它。

将解锁的互斥锁传递给 pthread_cond_wait

pthread_cond_wait(&gFinish, &gLockF);

这有四个我们已经提到的问题:

  • 缺少 gFinish
  • 的初始化
  • 缺少 gLockF
  • 的初始化
  • 缺少必需的循环
  • 缺乏外在条件

但事实并非如此。

pthread_cond_wait 需要锁定的互斥体,但您传递的是未锁定的互斥体。它将立即 return 并出现错误 EINVAL

无用代码

这又是关于

pthread_cond_wait(&gFinish, &gLockF);

完全不需要。 pthread_join 已经在等待线程完成。所以这没有用。


完整的最终代码:

#include <stdio.h>
#include <stdbool.h>
#include <pthread.h>

static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t  cond  = PTHREAD_COND_INITIALIZER;
static int prio_lvl = 3;

static int  gPos = 0;
static char savedExe[3];

static void wait_for_level(int a_prio_lvl) {
   pthread_mutex_lock(&mutex);
   while (prio_lvl > a_prio_lvl)
      pthread_cond_wait(&cond, &mutex);
   pthread_mutex_unlock(&mutex);
}

static void switch_to_level(int a_prio_lvl) {
   pthread_mutex_lock(&mutex);
   prio_lvl = a_prio_lvl;
   pthread_mutex_unlock(&mutex);
   pthread_cond_broadcast(&cond);
}

static void *pT1(void *arg) {
   (void)arg;
   wait_for_level(1);
   savedExe[gPos++] = '1';
   return NULL;
}

static void *pT2(void *arg) {
   (void)arg;
   wait_for_level(2);
   savedExe[gPos++] = '2';
   switch_to_level(1);
   return NULL;
}

static void *pT3(void *arg) {
   (void)arg;
   wait_for_level(3);
   savedExe[gPos++] = '3';
   switch_to_level(2);
   return NULL;
}

int main(void) {
   pthread_t tid1;
   pthread_t tid2;
   pthread_t tid3;

   pthread_create(&tid1, NULL, pT1, NULL);
   pthread_create(&tid2, NULL, pT2, NULL);
   pthread_create(&tid3, NULL, pT3, NULL);

   pthread_join(tid1, NULL);
   pthread_join(tid2, NULL);
   pthread_join(tid3, NULL);

   {
      const char *format = "%c";
      for (size_t i = 0; i < sizeof(savedExe); ++i) {
         printf(format, savedExe[i]);
         format = ", %c";
      }

      printf("\n");
   }

   return 0;
}

好的,我了解您的要求。您想以任何形式进行基于优先级的线程启动和存储结果。

您可以通过创建一个具有优先级值的结构的简单优先级队列和保存线程信息的 ptherad 变量来实现此目的。

唯一的问题是您需要在 c 语言中通过大致类似的结构创建一个自定义优先级队列

typedef struct prt_qu{
    uint8_t prty;
    pthread th;
} ptr_qu;

执行时只需要将所有任务放入优先队列,让主线程一个一个调用这些线程即可。只需使用 pthread_join().

就可以让 main 等待当前线程等待