2016-04-30 113 views
3

我有实现多线程Web代理的基本素描:在多线程信号处理程序中调用fflush?

FILE *proxy_log_file; 

static void 
SIGUSR1_handler(int sig) 
{ 
    (void)sig; 
    fflush(proxy_log_file); 
} 

int 
main(int argc, char **argv) 
{ 
    proxy_log_file = fopen("proxy.log", "a"); 
    Signal(SIGUSR1, SIGUSR1_handler); 
} 

的想法是,网络管理员可以使用kill命令将SIGUSR1信号发送到Web代理刷新缓冲的日志记录到日志文件。但是,我不确定在信号处理程序中调用fflush是个好主意。我知道fflush是线程安全的,但不认为它是异步信号安全的。在多线程的信号处理程序中调用fflush会产生什么并发性问题?

回答

3

假设你的线程调用一个标准的IO函数来锁定一个保护流数据结构的互斥锁。在解锁该互斥锁之前,会传递一个信号,并调用您的信号处理程序。您的信号处理程序调用fflush()并尝试锁定互斥锁。您的线程和标准IO流现在将永远死锁,因为您的信号处理程序将等待互斥量,但它永远不可用,因为您的线程将阻塞,直到信号处理程序返回。这是一个典型的僵局。

这就是线程和信号处理程序之间的区别。如果一个线程试图锁定一个互斥锁并且发现它已经被锁定,它就会进入休眠状态,其他线程将运行,并且持有该互斥锁的线程迟早会释放它。但是你的信号处理程序不是一个线程,所以它不会进入休眠状态并让中断的线程运行 - 该线程将直接阻塞,直到信号处理程序返回,在上例中它永远不会。

+0

因此,我需要阻止异步传递并创建一个专用线程,通过调用'sigwait'来同步接收信号? – user6269144

+1

不,您只需要调用不会对信号处理程序异步安全的函数。这样做并不安全。例如,您的信号处理程序可以设置一个标记,例如您已声明为volatile volatile sig_atomic_t,并且如果它检测到标记已设置,则其中一个线程可以刷新该流。或者,也可以使用信号以外的机制。 –

+0

您所写的内容仅适用于受互斥锁保护的流。后者自C11起标准化。在C11标准之前,代码可能不会死锁,但仍可能会出现其他一些奇怪的行为,例如刷新另一个线程的半填充缓冲区。 – alk