pthread_cancel () не отменяет поток как следует - PullRequest
0 голосов
/ 26 ноября 2018

Я тестирую обработчик сигнала для моего проекта 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 голосов
/ 27 ноября 2018

Запустив полную программу самостоятельно, оказывается, что единственная важная ошибка - та, которую я первоначально указал в комментариях.(Существует множество проблем со стилем и местами, где я бы так не поступил, но ни одно из них не поднимается до уровня «жучка». - это одна незначительная, не связанная ошибка: вы забылиinclude 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 fail: Not not 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;
}

signalHandlerTask и dummyTask в основном корректны, так какесть.

...