我有一个带有两个线程的C程序:主线程连续从网络读取数据并将其打印到屏幕上,而辅助线程则监听并处理来自标准输入的按键。
目前我的程序捕获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;
}
答案 0 :(得分:2)
我看到的一种方法是通过 poll()或 select()读取标准输入,并使用一些小的超时。当它在超时时返回时,您可以检查"运行"标志并执行清除终止,如果它被设置为" false"值。
可能它不是最佳解决方案,但它应该有效。
答案 1 :(得分:2)
有两种方法:
强制结束系统调用基本上可以通过两种方式完成:要么让系统调用无法完成(例如,关闭阻塞调用等待的文件描述符),要么发送一些信号给线程,并确保正确设置信号处理。正确的信号处理意味着信号不被忽略,未被屏蔽,并且信号标志的设置方式使得系统调用在信号处理后不会重新启动(参见sigaction(),SA_RESTART标志)。 / p>
答案 2 :(得分:1)
我设法找到一个适合我的解决方案:我从标准输入非阻塞中读取。
fcntl(fileno(stdin), F_SETFL, O_NONBLOCK);
这还需要在后台线程中使用某种形式的sleep
(或usleep
或nanosleep
)。
感谢HapKoM让我思考正确的方向。
答案 3 :(得分:1)
替换
if((c = fgetc(stdin)) != -1)
与
if (0 < read(fileno(stdin), &c, 1))
如果线程收到信号, read()
将被中断。