繁体   English   中英

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

[英]How to terminate threads cleanly in C?

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

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

  1. 第一个看PIR传感器的输出,如果检测到移动则需要拍照。 线程函数是task1() ;
  2. 第二个使用sigwaitalarm()来测量每一秒的温度。 线程函数是task2()
  3. 第三个线程检查是否拍摄了新照片,如果是,则执行其他一些操作。 与第一个线程的同步是通过全局标志,互斥锁和pthread_cond_wait 线程函数是task3()

所有线程函数都有一个无限循环。 程序的执行似乎很好。

主线程调用函数pause()然后pthread_cancel()从每个线程中干净地退出(降低引脚)。 起初我没有使用信号处理程序和进程退出而没有调用使用函数pthread_cleanup_push注册的退出线程函数。 这是因为pause()仅在处理程序返回时返回。 这就是为什么我添加了返回的信号处理程序。

通过这种方式正确调用pthread_cancel并且还正确调用了现有的线程函数(输出被打印),但即使按下CTRL-C或从另一个终端窗口调用kill,进程也会继续运行。

我想我搞砸了面具,这样pthread_cancel (如果有的话)产生的信号没有效果。

除此之外,我已经读过,一般来说使用pthread_cancel是不好的做法所以我的问题是:

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

任何建议将不胜感激。

编辑:如果不是调用pthread_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;
}

一般来说,我建议不要取消或杀死线程。 我还尝试最小化线程应用程序中的信号处理,或者至少使信号处理程序非常短,无阻塞且简单。 最好让线程运行一个循环,例如检查取消标志,或者如果你的线程使用select或epoll进行I / O操作,让主线程写入管道以指示另一端死亡。 使用C ++和pthreads,取消或终止可能更加灾难性,因此对于C ++来说,使用自定义代码进行干净关闭更为重要。

请参阅例如pthread cancel和C ++

您不能在清理函数中调用pthread_exit() ,因为pthread_exit()也将调用为该线程注册的清理函数。

因此,在您的程序中,清理函数是递归调用的,并且线程永远不会退出。

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

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

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

编辑:回答你的上一个评论。

使用标志退出线程task2()task3()似乎很复杂,因为主线程必须将SIGALRM发送到task2才能将其唤醒,并且还要发出条件信号以唤醒task3

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

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

使用标志,对于task3 ,可能存在以下问题:

  1. task3检查0的标志
  2. 主线程将标志设置为1
  3. 主线程发出信号
  4. task3开始等待这个条件

在这种情况下,线程task3将不会退出,因为它在条件发出信号时没有等待。 我不确定,但是可以通过使用我们用于条件的相同互斥锁来保护标志来避免这个问题。 因为当标志被设置并且条件发出信号时, task3将等待条件或从临界区开始工作。

我不知道task2是否存在问题,例如,如果信号由于内部问题而丢失,但通常情况下,信号将处于待处理状态。

这是我测试的代码。 我将1作为函数pthread_cleanup_pop() ,以使线程执行清理函数。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM