[英]How to terminate threads cleanly in C?
我正在嘗試用C語言為Raspberry Pi在raspbian環境(UNIX系統)中編寫一個多線程應用程序。
除主線程外,還創建了三個其他線程並執行以下操作:
task1()
; sigwait
和alarm()
來測量每一秒的溫度。 線程函數是task2()
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
,可能存在以下問題:
0
的標志 1
在這種情況下,線程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.