pthread_cancel()未按预期取消线程

fkaflof6  于 2022-12-03  发布在  其他
关注(0)|答案(1)|浏览(148)

我正在为我的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;
}
tp5buhyn

tp5buhyn1#

我自己运行了完整的程序,结果发现唯一重要的bug就是我最初在评论中指出的那个bug。(这里有很多风格问题和我不想那样做的地方,但没有一个上升到“bug”的级别。有一个无关的小bug:您忘记了包括stdio.hsignal.h。)

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

这将创建一个名为dummyThread的变量(不管它是否在循环中声明),并将所有线程句柄写入该变量。所有thids[i]指针都设置为指向该变量。由于一个pthread_t变量只能保存一个线程句柄,因此除了最后一次循环外,您将丢失在所有循环中创建的线程句柄。稍后,信号处理程序线程将尝试重复取消同一个线程,第一次成功,剩余的N-1次失败。(为了使所发生的事情更加明显,请增加线程的数量,并注意程序将输出“pthread_cancel failed:没有这样的过程”正好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基本上是正确。

相关问题