简体   繁体   English

信号捕获和线程终止有麻烦-C

[英]Trouble with signal catching and thread termination - C

I'm writing a program in c, which make use of threads, and i also want to catch Ctrl+C signal from the user. 我正在用c编写程序,该程序利用了线程,而且我还想捕获用户的Ctrl + C信号。 So, before i go multithreading, i make the signal catching. 因此,在我进行多线程处理之前,先进行信号捕获。

My main thread (i mean besides the actual main thread that the program runs on), is a method to deal with user input, and i also join this thread to the main program thread. 我的主线程(除了程序在其上运行的实际主线程之外,我的意思是)是一种处理用户输入的方法,并且我也将此线程连接到主程序线程。

The problem is, when testing and hitting Ctrl+C to exit program, the thread responsible for receiving user input doesn't close until i hit "return" on my keyboard - like its stuck on infinite loop. 问题是,当测试并按Ctrl + C退出程序时,负责接收用户输入的线程直到我按下键盘上的“ return”键时才关闭-就像卡在无限循环中一样。

When exiting by typing 'q', all threads end up properly. 通过键入“ q”退出时,所有线程均正确结束。

I use a global variable exit_flag to indicate the threads to finish their loops. 我使用全局变量exit_flag来指示线程完成其循环。

Also , in init_radio_stations method there's another single thread creation, that loops in the exact same way - on the exit_flag status, and this thread DOES close properly 同样 ,在init_radio_stations方法中,还有另一个单线程创建,该循环以完全相同的方式循环-在exit_flag状态下,此线程确实关闭

Here's my main loop code: 这是我的主循环代码:

void main_loop()
{
    status_type_t rs = SUCCESS;
    pthread_t thr_id;

    /* Catch Ctrl+C signals */
    if(SIG_ERR == signal(SIGINT, close_server)) {
        error("signal() failed! errno = ");
    }

    printf("\n~ Welcome to radio_server! ~\n Setting up %d radio stations... ", srv_params.num_of_stations);
    init_radio_stations();
    printf("Done!\n\n* Hit 'q' to exit the application\n* Hit 'p' to print stations & connected clients info\n");

    /* Create and join a thread to handle user input */
    if(pthread_create(&thr_id, NULL, &rcv_usr_input, NULL)) {
        error("main_loop pthread_create() failed! errno = ");
    }
    if(pthread_join(thr_id, NULL)) {
        error("main_loop pthread_join() failed! errno = ");
    }
}

close_server method: close_server方法:

void close_server(int arg)
{
    switch(arg) {
    case SIGINT: /* 2 */
        printf("\n^C Detected!\n");
        break;

    case ERR: /* -1 */
        printf("\nError occured!\n");
        break;

    case DEF_TO: /* 0 */
        printf("\nOperation timed-out!\n");
        break;

    default: /* will handle USER_EXIT, and all other scenarios */
        printf("\nUser abort!\n");
    }

    printf("Signaling all threads to free up all resources and exit...\n");

    /* Update exit_flag, and wait 1 sec just in case, to give all threads time to close */
    exit_flag = TRUE;
    sleep(1);
}

And rcv_usr_input handle code: 和rcv_usr_input处理代码:

void * rcv_usr_input(void * arg_p)
{
    char in_buf[BUFF_SIZE] = {0};

    while(FALSE == exit_flag) {
        memset(in_buf, 0, BUFF_SIZE);

        if(NULL == fgets(in_buf, BUFF_SIZE, stdin)) {
            error("fgets() failed! errno = ");
        }

        /* No input from the user was received */
        if('\0' == in_buf[0]) {
            continue;
        }

        in_buf[0] = tolower(in_buf[0]);
        if( ('q' == in_buf[0]) && ('\n' == in_buf[1]) ) {
            close_server(USER_EXIT);
        } else {
            printf("Invalid input!\nType 'q' or 'Q' to exit only\n");
        }
    }

    printf("User Input handler is done\n");
    return NULL;
}

I'm guessing my problem is related to joining the thread that uses rcv_usr_input at the end of my main loop, but i can't figure out what exactly causing this behavior. 我猜我的问题与在主循环末尾加入使用rcv_usr_input的线程有关,但是我无法弄清楚到底是什么导致了此行为。

I'll be glad to get some help, Thanks 我很高兴得到一些帮助,谢谢

Mike and Kaylum have correctly identified the fundamental problem of blocking by fgets() . Mike和Kaylum已经正确地确定了fgets()阻止的基本问题。 The larger issue remains, however: how to terminate a blocking thread when the process receives a SIGINT . 但是,更大的问题仍然存在:当进程收到SIGINT时如何终止阻塞线程。 There are several solutions. 有几种解决方案。

Thead Detachment: One solution is to detach the blocking thread because detached threads do not prevent the process from terminating when the last non-detached thread terminates. Thead分离:一种解决方案是分离阻塞线程,因为分离的线程不会阻止进程在最后一个非分离的线程终止时终止。 A thread is detached either by calling pthread_detach() on it, eg, 可以通过在线程上调用pthread_detach()来分离线程,例如,

#include <pthread.h>
// Called by pthread_create()
static void* start(void* arg)
{
    pthread_detach();
    ...
}

or by creating the thread with the PTHREAD_CREATE_DETACHED attribute, eg, 或通过创建具有PTHREAD_CREATE_DETACHED属性的线程,例如,

#include <pthread.h>
...
    pthread_attr_t attr;
    (void)pthread_attr_init(&attr);
    (void)pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    ...
    (void)pthread_t thread;
    (void)pthread_create(&thread, &attr, ...);

Note that pthread_join() should not be called on a detached thread. 请注意, pthread_join() 应该叫上分离线程。

Signal Forwarding: Another solution is not to detach the blocking thread but to forward signals like SIGINT to the thread via pthread_kill() if the signal has not been received on the blocking thread, eg, 信号转发:另一种解决方案不是分离阻塞线程,而是在阻塞线程上未收到信号的情况下,通过pthread_kill()将类似SIGINT信号转发到线程,例如,

#include <pthread.h>
#include <signal.h>
...
static pthread_t thread;
...
static void handle_sigint(int sig)
{
    if (!pthread_equal(thread, pthread_self()) // Necessary
        (void)pthread_kill(thread, SIGINT);
}
...
    sigaction_t sigaction;
    sigaction.sa_mask = 0;
    sigaction.sa_flags = 0;
    sigaction.sa_handler = handle_sigint;
    (void)sigaction(SIGHUP, &sigaction, ...);
    ...
    (void)pthread_create(&thread, ...);
    ...
    (void)pthread_join(thread, ...);
    ...

This will cause the blocking function to return with errno set to EINTR . 这将导致阻塞函数在errno设置为EINTR返回。

Note that use of signal() in a multi-threaded process is unspecified. 请注意,未指定在多线程进程中使用signal()

Thread Cancellation: Another solution is to cancel the blocking thread via pthread_cancel() , eg, 线程取消:另一个解决方案是通过pthread_cancel()取消阻塞线程,例如,

#include <pthread.h>
...
static void cleanup(...)
{
    // Release allocated resources
    ...
}
...
static void* start(void* arg)
{
    pthread_cleanup_push(cleanup, ...);
    for (;;) {
        ...
        // Call the blocking function
        ...
    }
    pthread_cleanup_pop(...);
    ...
}
....
static void handle_sigint(int sig)
{
    (void)pthread_cancel(thread);
}
...
    sigaction_t sigaction;
    sigaction.sa_mask = 0;
    sigaction.sa_flags = 0;
    sigaction.sa_handler = handle_sigint;
    (void)sigaction(SIGHUP, &sigaction, ...);
    ...
    (void)pthread_create(&thread, ..., start, ...);
    ...
    (void)pthread_join(thread, ...);
    ...

There is yet another solution for threads that block in a call to select() or poll() : create a file descriptor on which the blocking function also waits and close that descriptor upon receipt of an appropriate signal -- but that solution is, arguably, beyond the scope of this question. 对于在调用select()poll()阻塞的线程还有另一种解决方案:创建一个文件描述符,阻塞函数也将在该文件描述符上等待,并在接收到适当的信号后关闭该描述符-但是,可以说该解决方案是有争议的,超出了此问题的范围。

The explanation is straight forward. 解释很简单。

fgets(in_buf, BUFF_SIZE, stdin);

That call blocks the thread until it receives a line of input. 该调用将阻塞线程,直到接收到输入行。 That is, it does not return until a newline is input or BUFF_SIZE-1 characters are input. 也就是说,它直到输入换行符或输入BUFF_SIZE-1字符BUFF_SIZE-1返回。

So even though the signal handler sets exit_flag to FALSE , the rcv_usr_input thread will not see that until it unblocks from fgets . 因此,即使信号处理程序将exit_flag设置为FALSErcv_usr_input线程也不会看到它,直到它从fgets rcv_usr_input阻止为止。 Which happens when you pressed "return". 当您按下“返回”时会发生这种情况。

According to http://www.cplusplus.com/reference/cstdio/fgets/ , fgets blocks until the specified number of bytes have been read. 根据http://www.cplusplus.com/reference/cstdio/fgets/,fgets会阻塞,直到读取了指定的字节数为止。

I suggest trying fread or some other input reception function that isn't blocking and then read only one byte at a time. 我建议尝试读取或其他不会阻塞的输入接收功能,然后一次只读取一个字节。 Here's sample code to help you: 以下示例代码可为您提供帮助:

if (fread(in_buf, 1,1, stdin) > 0){
//character has been read
}

And I wouldn't worry about the extra sleep statement in your signal handler as it causes delays in forceful exiting at best. 而且我不会担心信号处理程序中额外的sleep语句,因为它最多会导致强制退出的延迟。

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

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