У меня есть программа C с двумя потоками: основной поток непрерывно считывает данные из сети и выводит их на экран, в то время как вторичный поток прослушивает и обрабатывает нажатия клавиш со стандартного ввода.Как прервать фоновый поток, блокирующий IO?
В настоящее время моя программа ловит SIGINT, SIGTERM и SIGPIPE, чтобы закончить программу чисто. Моя проблема заключается в том, что в конце основного потока (как только основной цикл завершился из обработчика сигнала), он пытается вернуть настройки терминала с помощью tcsetattr
, однако это блокируется до тех пор, пока текущий fgetc
не вызовет другой поток.
Как я могу прерывать фоновый поток так, чтобы вызов fgetc
возвращался, и основной поток мог восстановить настройки терминала и выйти из него чисто?
Я попытался использовать pthread_kill(thread, SIGINT)
, но это просто приводит к тому, что мой существующий обработчик сигналов будет вызван снова.
Соответствующий код:
// If the program should still be running.
static sig_atomic_t running = 1;
// Background thread that reads keypresses.
pthread_t thread;
static void *get_keypresses();
static void receive_signal(int signal) {
(void)signal;
running = 0;
}
int main(int argc, char *argv[]) {
// Set up signal handling.
if(signal(SIGINT, receive_signal) == SIG_ERR) {
fprintf(stderr, "Error setting signal handler for SIGINT.\n");
}
if(signal(SIGTERM, receive_signal) == SIG_ERR) {
fprintf(stderr, "Error setting signal handler for SIGTERM.\n");
}
if(signal(SIGPIPE, receive_signal) == SIG_ERR) {
fprintf(stderr, "Error setting signal handler for SIGPIPE.\n");
}
// Set up thread attributes.
pthread_attr_t thread_attrs;
if(pthread_attr_init(&thread_attrs) != 0) {
perror("Unable to create thread attributes");
exit(2);
}
if(pthread_attr_setdetachstate(&thread_attrs, PTHREAD_CREATE_DETACHED) != 0) {
perror("Unable to set thread attributes");
exit(2);
}
// Set up terminal for reading keypresses.
struct termios orig_term_attr;
struct termios new_term_attr;
tcgetattr(fileno(stdin), &orig_term_attr);
memcpy(&new_term_attr, &orig_term_attr, sizeof(struct termios));
new_term_attr.c_lflag &= ~(ECHO|ICANON);
tcsetattr(fileno(stdin), TCSANOW, &new_term_attr);
// Start background thread to read keypresses.
if((pthread_create(&thread, &thread_attrs, &get_keypresses, NULL)) != 0) {
perror("Unable to create thread");
exit(2);
}
// Main loop.
while(running) {
// Read data from network and output to screen.
}
// Restore original terminal attributes. ***IT BLOCKS HERE***
tcsetattr(fileno(stdin), TCSANOW, &orig_term_attr);
return 0;
}
// Get input from the keyboard.
static void *get_keypresses() {
int c;
while(running) {
// Get keypress. ***NEEDS TO BE INTERRUPTED HERE***
if((c = fgetc(stdin)) != - 1) {
// Handle keypress.
}
}
return NULL;
}
Здесь есть сообщение, описывающее метод, который можно использовать для прерывания. Это можно использовать в сочетании с флагом или подобным набором, например, тем, что предложил @HapKom. Также предлагайте использовать Sleep(); после того, как вы выполнили действие в своей рабочей функции потока, например. прямо под вашим комментарием '// Handle keypress'. – ryyker