如何在C中干净地终止线程?

如何在C中干净地终止线程?,c,multithreading,raspberry-pi,pthreads,raspbian,C,Multithreading,Raspberry Pi,Pthreads,Raspbian,我正在尝试在raspbian环境(UNIX系统)中为Raspberry Pi编写一个C多线程应用程序 除主线程外,还创建了三个其他线程,并执行以下操作: 首先查看PIR传感器的输出,如果检测到移动,则拍摄照片。线程函数是task1() 第二个使用sigwait和alarm()每给定秒测量一次温度。线程函数是task2() 第三个线程检查是否拍摄了一张新照片,如果是的话,它会做一些其他的事情。与第一个线程的同步是通过全局标志、互斥和pthread\u cond\u wait完成的。线程函数是tas

我正在尝试在raspbian环境(UNIX系统)中为Raspberry Pi编写一个C多线程应用程序

除主线程外,还创建了三个其他线程,并执行以下操作:

  • 首先查看PIR传感器的输出,如果检测到移动,则拍摄照片。线程函数是
    task1()
  • 第二个使用
    sigwait
    alarm()
    每给定秒测量一次温度。线程函数是
    task2()
  • 第三个线程检查是否拍摄了一张新照片,如果是的话,它会做一些其他的事情。与第一个线程的同步是通过全局标志、互斥和
    pthread\u cond\u wait
    完成的。线程函数是
    task3()
  • 所有线程函数都有一个无限循环。程序的执行似乎很好

    主线程调用函数
    pause()
    ,然后调用
    pthread\u cancel()
    ,以从每个线程干净地退出(降低管脚)。 起初,我没有使用信号处理程序,进程在没有调用使用函数
    pthread\u cleanup\u push注册的退出线程函数的情况下退出。这是因为
    pause()
    仅当处理程序返回时才返回。这就是为什么我添加了返回的信号处理程序

    通过这种方式,可以正确调用
    pthread\u cancel
    ,也可以正确调用现有的线程函数(打印输出),但即使按下CTRL-C或从另一个终端窗口调用kill,进程也会保持运行

    我想我弄乱了掩码,所以
    pthread\u cancel
    (如果有的话)生成的信号没有效果

    除此之外,我还了解到使用
    pthread\u cancel
    通常是不好的做法,因此我的问题是:

    从每个线程干净退出的最佳方式是什么(尤其是在我的情况下)?我应该使用另一面全球旗帜吗?使用互斥锁还是读写锁?我应该从主线程或处理程序设置它吗?

    如有任何建议,将不胜感激

    编辑:如果我没有调用
    pthread\u cancel
    而是为无限循环使用全局标志,您将如何在
    task3()
    中设置条件

    注意:为了简洁起见,代码不完整。我试图强调逻辑。如果需要,我会添加所有的代码

    #include<wiringPi.h>  
    #include<stdlib.h>
    #include<stdio.h>
    #include<signal.h>
    #include<stdint.h>
    #include<pthread.h>
    
    g_new_pic_flag=FALSE;
    pthread_cond_t g_new_pic_cond = PTHREAD_COND_INITIALIZER;
    pthread_mutex_t g_new_pic_m = PTHREAD_MUTEX_INITIALIZER;
    
    /* FUNCTION DECLARATION */
    
    /*We define thread exit functions so that each pin 
    is lowered by the thread in which it is used avoiding
    race condition between the signal handler of the main thread
    and the other threads*/
    void exitingThreadTask1(void* arg);
    void exitingThreadTask2(void* arg);
    void exitingThreadTask3(void* arg);
    
    void* task1(void *arg); //thread function for the motion sensor
    void* task2(void *arg); //thread function for the temperature reading
    void* task3(void *arg); //thread function to post data on IOT platforms
    
    /*Signal handler to return from pause*/
    void sig_handler(int signo);
    
    int main()
    {
        int err;
        sigset_t omask, mask;
        pthread_t thread_motionSensor;
        pthread_t thread_tempReading;
        pthread_t thread_platformPost;
    
        printf("Created threads IDs\n");
    
        if (wiringPiSetup()<0)
        {
            printf("WiringPi error\n");
            return -1;
        }
        printf("WiringPi is ok\n");
    
        if (signal(SIGQUIT, sig_handler)==SIG_ERR)
            printf("Error on recording SIGQUITHANDLER\n");
        if (signal(SIGINT, sig_handler)==SIG_ERR)
            printf("Error on recording SIGINTHANDLER\n");
        if (signal(SIGTERM, sig_handler)==SIG_ERR)
            printf("Error on recording SIGTERMHANDLER\n");
    
        /*Create a new mask to block all signals for the following thread*/
        sigfillset(&mask);
        pthread_sigmask(SIG_SETMASK, &mask, &omask);
        printf("Trying to create threads\n");
        if ((err = pthread_create (&thread_motionSensor, NULL, task1, NULL))!=0)
        {
        printf("Thread 1 not created: error %d\n", err);
            err_exit((const char)err, "pthread_create error");
        }
        printf("Thread 1 created. Trying to create Thread 2\n");
        if((err = pthread_create (&thread_tempReading,   NULL, task2, NULL))!=0)
        {
        printf("Thread 2 not created: error %d\n", err);
            err_exit((const char)err, "pthread_create error");
        }
        printf("Thread 2 created. Trying to create Thread 3\n");
        if ((err = pthread_create (&thread_platformPost, NULL, task3, NULL))!=0)
        {
         printf("Thread 3 not created: error %d %d\n", err);
             err_exit((const char)err, "pthread_create error");
        }
        printf("Thread 3 created\n");
        /*The main thread must block the SIGALRM but catch SIGINT
        SIGQUIT, SIGTERM, SIgkILL*/
        sigemptyset(&omask);
        sigaddset(&omask, SIGINT);
        sigaddset(&omask, SIGQUIT);
        sigaddset(&omask, SIGKILL);
        sigaddset(&omask, SIGTERM);
    
        pthread_sigmask(SIG_UNBLOCK, &omask, NULL);
        printf("Main thread waiting for signal\n");
        pause();
        printf("Exit signal received: cancelling threads\n");
    
        pthread_cancel(thread_motionSensor);
        pthread_cancel(thread_tempReading);
        pthread_cancel(thread_platformPost);
        pthread_join(thread_motionSensor, NULL);
        pthread_join(thread_tempReading,  NULL);
        pthread_join(thread_platformPost, NULL);
        printf("Exiting from main thread and process\n");
        exit(0);
    }
    
    void* task1(void *arg)
    {
        //INITIALIZING
        pthread_cleanup_push(exitingThreadTask1, NULL);
        while(1)
        {
            //do stuff1
        }
        pthread_cleanup_pop(0);
        pthread_exit(0);
    
    }
    
    void* task2(void *arg)
    {
        static const unsigned char schedule_time = 5;
        int signo, err;
        /*
        We set a local mask with SIGALARM for the function sigwait
        All signals have already been blocked
        */
        sigset_t alarm_mask;
        sigemptyset(&alarm_mask);
        sigaddset(&alarm_mask, SIGALRM);
        alarm(schedule_time);
        pthread_cleanup_push(exitingThreadTask2, NULL);
        while (1)
        {
            err = sigwait(&alarm_mask, &signo); //signo == SIGALRM check
            if (err!=0)
                err_exit(err, "sigwait failed\n");
            //do stuff
            alarm(schedule_time);
        }
        pthread_cleanup_pop(0);
        pthread_exit(0);
    }
    
    void* task3(void *arg)
    {
        pthread_cleanup_push(exitingThreadTask3, NULL);
        while(1)
        {
            pthread_mutex_lock(&g_new_pic_m);
            while(g_new_pic_flag==FALSE)
            {
                pthread_cond_wait(&g_new_pic_cond, &g_new_pic_m);
            }
            pthread_mutex_unlock(&g_new_pic_m);
            //do stuff
        }
        pthread_cleanup_pop(0);
        pthread_exit(0);
    
    }
    
    void exitingThreadTask1(void* arg)
    {
        printf("Thread of task 1 exiting\n");
        digitalWrite(OUTPIN, LOW);
        digitalWrite(INPIN, LOW);
        printf("Pins lowered\n");
        pthread_exit((void*)0);
    }
    
    void exitingThreadTask2(void* arg)
    {
        printf("Thread of task 2 exiting\n");
        digitalWrite(DHTPIN, LOW);
        printf("Pin lowered\n");
        pthread_exit((void*)0);
    }
    
    void exitingThreadTask3(void* arg)
    {
        printf("Thread of task 3 exiting\n");
        pthread_exit((void*)0);
    }
    
    void sig_handler(int signo)
    {
        printf("Running handler to return from pause\n");
        return;
    }
    
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    g_new_pic_flag=FALSE;
    pthread_cond_t g_new_pic_cond=pthread_cond_初始值设定项;
    pthread_mutex_t g_new_pic_m=pthread_mutex_初始值设定项;
    /*函数声明*/
    /*我们定义了线程退出函数,以便每个引脚
    由使用它的螺纹降下
    主线程的信号处理程序之间的争用条件
    其他的线呢*/
    无效退出线程任务1(无效*arg);
    无效退出线程任务2(无效*arg);
    无效退出线程任务3(无效*arg);
    void*task1(void*arg)//运动传感器的线程功能
    void*task2(void*arg)//用于温度读数的线程函数
    void*task3(void*arg)//在物联网平台上发布数据的线程函数
    /*从暂停返回的信号处理程序*/
    无效信号处理器(int signo);
    int main()
    {
    INTERR;
    sigset_t OMAK,面罩;
    pthread_t thread_运动传感器;
    pthread_t thread_tempreding;
    pthread_t thread_platformPost;
    printf(“创建的线程ID\n”);
    
    如果(wiringPiSetup()您不能在清理函数中调用
    pthread\u exit()
    ,因为
    pthread\u exit()
    也将调用为线程注册的清理函数

    因此,在您的程序中,将递归调用cleanup函数,线程永远不会退出

    关于来自另一个终端的kill,命令
    kill-9
    和进程的pid应该始终有效,因为SIGKILL不能被忽略或捕获

    在信号处理函数中,必须使用异步信号安全函数,
    printf()
    不是异步信号安全函数

    在主线程中等待信号的另一种方法是使用
    sigwait()
    sigwaitinfo()
    而不是
    pause()
    ,就像在线程中对SIGALARM所做的那样。因此,它不需要注册处理函数,但需要阻止所有线程中捕获的信号

    编辑:回答您最后的评论。 使用标志退出线程
    task2()
    task3()
    似乎很复杂,因为主线程必须将SIGALRM发送到
    task2
    才能唤醒它,还必须发出条件信号才能唤醒
    task3

    我修改了您的代码以尝试使用标志,但我可能错过了一个最终的问题,因为同步线程可能很复杂

    对于您的程序,我没有足够的知识来说明使用
    pthread\u cancel()
    pthread\u testcancel()
    ,或者使用标志是否更好。但是,
    pthread\u cancel()
    似乎能够在没有同步问题的情况下取消,线程正在等待信号或条件

    对于
    task3
    ,使用标志可能会出现以下问题:

  • 任务3检查
    0
  • 主线程将标志设置为
    1
  • 主线程发出状态信号
  • 任务3开始等待条件
  • 在这种情况下,线程
    task3
    将不会退出,因为它在通知条件时没有等待。我不确定,但可以通过使用我们用于条件的相同互斥来保护标志来避免此问题。因为当设置标志并通知条件时,
    task3
    将等待条件或条件在关键区域外做作业

    我不知道任务2是否有问题,例如,如果信号由于内部问题而丢失,但通常情况下,信号将挂起

    这是我的测试代码。我把
    1
    
    #include<stdlib.h>
    #include<stdio.h>
    #include<signal.h>
    #include<stdint.h>
    #include<pthread.h>
    
    #define FALSE 0
    volatile sig_atomic_t g_new_pic_flag=FALSE;
    pthread_cond_t g_new_pic_cond = PTHREAD_COND_INITIALIZER;
    pthread_mutex_t g_new_pic_m = PTHREAD_MUTEX_INITIALIZER;
    volatile int g_shutdown_task_3 = 0;
    
    volatile int g_shutdown_task_1_2 = 0;
    pthread_mutex_t g_shutdown_mutex = PTHREAD_MUTEX_INITIALIZER;
    /* FUNCTION DECLARATION */
    
    /*We define thread exit functions so that each pin 
    is lowered by the thread in which it is used avoiding
    race condition between the signal handler of the main thread
    and the other threads*/
    void exitingThreadTask1(void* arg);
    void exitingThreadTask2(void* arg);
    void exitingThreadTask3(void* arg);
    
    void* task1(void *arg); //thread function for the motion sensor
    void* task2(void *arg); //thread function for the temperature reading
    void* task3(void *arg); //thread function to post data on IOT platforms
    
    /*Signal handler to return from pause*/
    void sig_handler(int signo);
    
    void err_exit(char err, char *msg) {
      printf("\nError: %s\n",msg);
      exit(1);
    }
    
    int main()
    {
        int err;
        sigset_t omask, mask;
        pthread_t thread_motionSensor;
        pthread_t thread_tempReading;
        pthread_t thread_platformPost;
    
        printf("Created threads IDs\n");
        /*
        if (wiringPiSetup()<0)
        {
            printf("WiringPi error\n");
            return -1;
        }
        */
        printf("WiringPi is ok\n");
    
        if (signal(SIGQUIT, sig_handler)==SIG_ERR)
            printf("Error on recording SIGQUITHANDLER\n");
        if (signal(SIGINT, sig_handler)==SIG_ERR)
            printf("Error on recording SIGQUITHANDLER\n");
        if (signal(SIGTERM, sig_handler)==SIG_ERR)
            printf("Error on recording SIGQUITHANDLER\n");
    
        /*Create a new mask to block all signals for the following thread*/
        sigfillset(&mask);
        pthread_sigmask(SIG_SETMASK, &mask, &omask);
        printf("Trying to create threads\n");
        if ((err = pthread_create (&thread_motionSensor, NULL, task1, NULL))!=0)
        {
        printf("Thread 1 not created: error %d\n", err);
            err_exit((const char)err, "pthread_create error");
        }
        printf("Thread 1 created. Trying to create Thread 2\n");
        if((err = pthread_create (&thread_tempReading,   NULL, task2, NULL))!=0)
        {
        printf("Thread 2 not created: error %d\n", err);
            err_exit((const char)err, "pthread_create error");
        }
        printf("Thread 2 created. Trying to create Thread 3\n");
        if ((err = pthread_create (&thread_platformPost, NULL, task3, NULL))!=0)
        {
         printf("Thread 3 not created: error %d %d\n", err);
             err_exit((const char)err, "pthread_create error");
        }
        printf("Thread 3 created\n");
        /*The main thread must block the SIGALRM but catch SIGINT
        SIGQUIT, SIGTERM, SIgkILL*/
        sigemptyset(&omask);
        sigaddset(&omask, SIGINT);
        sigaddset(&omask, SIGQUIT);
        sigaddset(&omask, SIGKILL);
        sigaddset(&omask, SIGTERM);
    
        pthread_sigmask(SIG_UNBLOCK, &omask, NULL);
        printf("Main thread waiting for signal\n");
        pause();
        printf("Exit signal received: cancelling threads\n");
        
        
        pthread_mutex_lock(&g_shutdown_mutex);
        g_shutdown_task_1_2 = 1;
        pthread_mutex_unlock(&g_shutdown_mutex);
        pthread_mutex_lock(&g_new_pic_m);
        g_shutdown_task_3 = 1;
        pthread_cond_signal(&g_new_pic_cond);
        pthread_mutex_unlock(&g_new_pic_m);
        
        pthread_kill(thread_tempReading,SIGALRM);
        
    
        pthread_join(thread_motionSensor, NULL);
        pthread_join(thread_tempReading,  NULL);
        pthread_join(thread_platformPost, NULL);
        printf("Exiting from main thread and process\n");
        exit(0);
    }
    
    void* task1(void *arg)
    {
        //INITIALIZING
        pthread_cleanup_push(exitingThreadTask1, NULL);
        while(1)
        {
            pthread_mutex_lock(&g_shutdown_mutex);
            if(g_shutdown_task_1_2) {
              pthread_mutex_unlock(&g_shutdown_mutex);
              break;
            }
            pthread_mutex_unlock(&g_shutdown_mutex);
            //do stuff1
            sleep(1);
        }
        pthread_cleanup_pop(1);
        pthread_exit(0);
    
    }
    
    void* task2(void *arg)
    {
        static const unsigned char schedule_time = 5;
        int signo, err;
        /*
        We set a local mask with SIGALARM for the function sigwait
        All signals have already been blocked
        */
        sigset_t alarm_mask;
        sigemptyset(&alarm_mask);
        sigaddset(&alarm_mask, SIGALRM);
        alarm(schedule_time);
        pthread_cleanup_push(exitingThreadTask2, NULL);
        while (1)
        {
            pthread_mutex_lock(&g_shutdown_mutex);
            if(g_shutdown_task_1_2) {
              pthread_mutex_unlock(&g_shutdown_mutex);
              break;
            }
            pthread_mutex_unlock(&g_shutdown_mutex);
            
            err = sigwait(&alarm_mask, &signo); //signo == SIGALRM check
            if (err!=0)
                err_exit(err, "sigwait failed\n");
            
            pthread_mutex_lock(&g_shutdown_mutex);
            if(g_shutdown_task_1_2) {
              pthread_mutex_unlock(&g_shutdown_mutex);
              break;
            }
            pthread_mutex_unlock(&g_shutdown_mutex);
            
            //do stuff
            alarm(schedule_time);
        }
        pthread_cleanup_pop(1);
        pthread_exit(0);
    }
    
    void* task3(void *arg)
    {
        pthread_cleanup_push(exitingThreadTask3, NULL);
        while(1)
        {
            pthread_mutex_lock(&g_new_pic_m);
            if(g_shutdown_task_3) {
              pthread_mutex_unlock(&g_new_pic_m);
              break;
            }
            while(g_new_pic_flag==FALSE)
            {
                if(g_shutdown_task_3) break;
                            
                pthread_cond_wait(&g_new_pic_cond, &g_new_pic_m);
                
                if(g_shutdown_task_3) break;
            }
            if(g_shutdown_task_3) {
              pthread_mutex_unlock(&g_new_pic_m);
              break;
            }
            pthread_mutex_unlock(&g_new_pic_m);
            //do stuff
        }
        pthread_cleanup_pop(1);
        pthread_exit(0);
    
    }
    
    void exitingThreadTask1(void* arg)
    {
        printf("Thread of task 1 exiting\n");
        //digitalWrite(OUTPIN, LOW);
        //digitalWrite(INPIN, LOW);
        printf("Pins lowered\n");
    }
    
    void exitingThreadTask2(void* arg)
    {
        printf("Thread of task 2 exiting\n");
        //digitalWrite(DHTPIN, LOW);
        printf("Pin lowered\n");
    }
    
    void exitingThreadTask3(void* arg)
    {
        printf("Thread of task 3 exiting\n");
    }
    
    void sig_handler(int signo)
    {
        return;
    }