我正在尝试用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;
}
答案 0 :(得分:4)
一般情况下,我建议不要取消或删除线程。我还尝试最小化线程应用程序中的信号处理,或者至少使信号处理程序非常短,无阻塞且简单。最好让线程运行一个循环,例如它们检查取消标志,或者如果您的线程使用select或epoll进行I / O操作,请让主线程写入管道以指示另一端死亡。使用C ++和pthreads,取消或终止可能更加灾难性,因此对于C ++来说,使用自定义代码进行干净关闭更为重要。
答案 1 :(得分:2)
你不能在清理函数中调用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;
}