pthread_cancel()并没有取消线程

时间:2018-11-26 20:06:04

标签: c multithreading signals posix

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

很遗憾,我的代码无法正常工作。

这是我的虚拟线程任务,我想以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;
}

1 个答案:

答案 0 :(得分:2)

我自己运行了完整程序,原来唯一重要的错误是我最初在注释中指出的错误。 (有很多样式问题和我无法做到的地方,但没有一个上升到“错误”的水平。有一个 个小无关的错误:您忘记了包括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次失败。 (要弄清楚发生了什么,请增加线程数,并注意该程序将N精确地打印“ 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基本上是正确的。