代码之家  ›  专栏  ›  技术社区  ›  Giulio

pthread_cancel()不会像应该的那样取消线程

  •  1
  • Giulio  · 技术社区  · 7 年前

    我正在为我的OS类项目测试一个信号处理程序。

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

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

     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
            }
        }
    }
    

    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 回复  |  直到 7 年前
        1
  •  2
  •   zwol    7 年前

    我自己运行了整个程序,结果发现唯一重要的bug就是我最初在评论中指出的那个bug(有很多风格上的问题和地方我都不会那样做,但没有一个能达到“bug”的程度。那里 stdio.h signal.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:No-this process”N-1次,无论N是多少。)

    纠正方法只是使用一个数组 pthread\u 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;
    }
    

    signalHandlerTask dummyTask 基本上是正确的。