pthread_cancel() 没有取消线程,因为它应该

pthread_cancel() does not cancel a thread as it should

我正在为我的 OS Class 项目测试信号处理程序。 基本上我的信号处理程序(在它自己的线程中 运行ning)必须处理 SIGINT,这意味着它必须 "kill" 所有其他线程然后退出。

不幸的是我的代码不起作用。

这是我的虚拟线程任务,我想用 SIGINT 停止它

 static void * dummyTask(){
   pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
   pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
   while(1){
      pthread_testcancel();
      printf(" Hello!\n");
      sleep(2);
   }
   return NULL;
}

这是我的主要内容。如您所见,我创建了信号处理程序,然后创建了最多 2 个虚拟线程。 我将它们的 thID 保存在一个数组中,我稍后在信号处理程序线程的任务中需要它,如您所见

 int main(){
   //need to the signal handler thread, so it can kill all the threads with cancel
   pthread_t **thids = malloc( 2 * sizeof(pthread_t *));
   sigset_t set;
   pthread_t signalHandlerThread;

   sigemptyset(&set);
   sigaddset(&set, SIGINT);
   //s is for error checking, not importanto now
   int s = pthread_sigmask(SIG_BLOCK, &set, NULL);

   //shParam: signalHandlerParam
   signalHParam *shParam = malloc(sizeof(signalHParam));
    shParam->set = &set;
    shParam->arrayOfThIDs = thids;
   s = pthread_create(&signalHandlerThread, NULL, signalHandlerTask, (void *) shParam);

   for(int i = 0; i < 2; i ++){
     pthread_t dummyThread;
     pthread_create(&dummyThread, NULL, &dummyTask, NULL);
     thids[i] = &dummyThread;
   }

   pause();
   //pthread_join(signalHandlerThread, NULL);
   return 1;
}

如您所见,signalHandlerThread 执行了一个名为 signalHandlerTask 的函数,即:

static void *signalHandlerTask(void *shParam){
   signalHParam *tmp = (signalHParam *) shParam;
   sigset_t *set = tmp->set;
   int s, sig;

    int i = sigismember(set, SIGINT);
    if(i != 1)
      printf("error\n");
   while(1 == 1){
      s = sigwait(set, &sig);

        if(sig == SIGINT){
            printf("\n----- signal recived ----\n");
         //function that use the array to kill threads
            killThreads(tmp->arrayOfThIDs);
            pthread_exit(NULL); //kill the signal handler thread
        }
    }
}

shParam 是我用来将多个参数传递给线程任务 (signalHandlerTask) 的结构,它是这样的

typedef struct{
   pthread_t **arrayOfThIDs;
   sigset_t *set;
} signalHParam;

我们终于到了真正的问题。我按如下方式创建了 killThreads 函数:

void killThreads(pthread_t **thids){
    for(int i = 0; i < 2; i++){
        int r = pthread_cancel(*thids[i]);
        if(r != 0)
            printf("error!! %d\n", r);
            //r is 3, why??
            pthread_join(*thids[i], NULL);
    }
}

问题是,如前所述,pthread_cancel(*thids[i]) 不工作,线程保持活动状态,我不明白为什么

这里是完整的代码,供那些想要 运行 的人使用:

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <error.h>
#include <unistd.h>
#include <errno.h>

typedef struct{
   pthread_t **arrayOfThIDs;
   sigset_t *set;
} signalHParam;

void killThreads(pthread_t **thids){
    //termino il threadListener, e tutti i thread nel thread pool 
    for(int i = 0; i < 2; i++){

        //FAI ANCHE SU PROGETTO!!
        int r = pthread_cancel(*thids[i]);
        if(r != 0)
         printf("pthread_cancel failed: %s\n", strerror(r));
        //FAI ANCHE SU PROGETTO!!
        pthread_join(*thids[i], NULL);
    }

}

static void *signalHandlerTask(void *shParam){
    signalHParam *tmp = (signalHParam *) shParam;
   sigset_t *set = tmp->set;
   int s, sig;

    int i = sigismember(set, SIGINT);
    if(i != 1)
      printf("error\n");
   while(1 == 1){
      s = sigwait(set, &sig);

        if(sig == SIGINT){
            printf("\n----- signal recived ----\n");
         //function that use the array to kill threads
            killThreads(tmp->arrayOfThIDs);
            pthread_exit(NULL); //kill the signal handler thread
        }
    }
}
static void * dummyTask(){
   pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
   pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
   //printf("mo aspetto 10s\n");
   //sleep(10);
   while(1){
      pthread_testcancel();
      printf(" Ciao!\n");
      sleep(2);
   }
   return NULL;
}

int main(){
   //need to the signal handler thread, so it can kill all the threads with cancel
   pthread_t **thids = malloc( 2 * sizeof(pthread_t *));
   sigset_t set;
   pthread_t signalHandlerThread;

   sigemptyset(&set);
   sigaddset(&set, SIGINT);
   //s is for error checking, not importanto now
   int s = pthread_sigmask(SIG_BLOCK, &set, NULL);

   //shParam: signalHandlerParam
   signalHParam *shParam = malloc(sizeof(signalHParam));
    shParam->set = &set;
    shParam->arrayOfThIDs = thids;
   s = pthread_create(&signalHandlerThread, NULL, signalHandlerTask, (void *) shParam);

   for(int i = 0; i < 2; i ++){
     pthread_t dummyThread;
     pthread_create(&dummyThread, NULL, &dummyTask, NULL);
     thids[i] = &dummyThread;
   }

   pthread_join(signalHandlerThread, NULL);
   return 1;
}

我自己有 运行 完整的程序,事实证明唯一重要的错误是我最初在评论中指出的错误。 (有很多样式问题和地方我不会那样做,但其中 none 上升到 "bug" 的水平。 一个不相关的小错误:您忘记包含 stdio.hsignal.h。)

for(int i = 0; i < 2; i ++){
  pthread_t dummyThread;
  pthread_create(&dummyThread, NULL, &dummyTask, NULL);
  thids[i] = &dummyThread;
}

这将创建一个名为 dummyThread 的变量(无论它是否在循环内声明)并将线程句柄的 all 写入该变量。所有 thids[i] 指针都设置为指向那个变量。由于单个 pthread_t 变量只能保存一个线程句柄,因此您将丢失在循环的最后一次迭代之外的所有线程上创建的线程句柄。稍后,信号处理程序线程将反复尝试取消同一个线程,第一次成功,其余 N-1 次失败。 (为了使发生的事情更明显,增加线程数并注意程序打印 "pthread_cancel failed: No such process" 恰好 N-1 次,无论 N 是多少。)

更正只是简单的用pthread_t的数组代替pthread_t *的数组,将线程句柄直接写入数组:

typedef struct{
   pthread_t *arrayOfThIDs;
   sigset_t *set;
} signalHParam;

void killThreads(pthread_t **thids){
    //termino il threadListener, e tutti i thread nel thread pool 
    for(int i = 0; i < 2; i++){
        //FAI ANCHE SU PROGETTO!!
        int r = pthread_cancel(thids[i]);
        if(r != 0)
         printf("pthread_cancel failed: %s\n", strerror(r));
        //FAI ANCHE SU PROGETTO!!
        pthread_join(*thids[i], NULL);
    }

}

// ...

int main(){
   //need to the signal handler thread, so it can kill all the threads with cancel
   pthread_t *thids = malloc( 2 * sizeof(pthread_t));
   sigset_t set;
   pthread_t signalHandlerThread;

   sigemptyset(&set);
   sigaddset(&set, SIGINT);
   //s is for error checking, not importanto now
   int s = pthread_sigmask(SIG_BLOCK, &set, NULL);

   //shParam: signalHandlerParam
   signalHParam *shParam = malloc(sizeof(signalHParam));
    shParam->set = &set;
    shParam->arrayOfThIDs = thids;
   s = pthread_create(&signalHandlerThread, NULL, signalHandlerTask,
                      (void *) shParam);

   for(int i = 0; i < 2; i ++){
     pthread_create(&thids[i], NULL, &dummyTask, NULL);
   }

   pthread_join(signalHandlerThread, NULL);
   return 1;
}

signalHandlerTaskdummyTask 基本上是正确的。