多线程和多处理时 fprintf 的行为如何?

Posted

技术标签:

【中文标题】多线程和多处理时 fprintf 的行为如何?【英文标题】:how fprintf behavior when multi-threaded and multi-processed? 【发布时间】:2020-07-31 00:35:03 【问题描述】:

这里是进程ab,它们都是多线程的。

    a forks bb 立即执行一个新程序; a dups 和 freopens stderr 到日志文件(a 是事实上的 apache 的 httpd2.22) ba 继承打开的标准错误。 (我正在适配 apache httpd,b 是我的程序),b 使用 fprintf(stderr....) 进行日志记录 所以ab 共享同一个文件进行日志记录 ab没有写日志的锁机制

我发现有些log msg是交错的,还有一点log msg丢失了。

同一文件的两个写入者可以隐式地相互锁定吗?

更重要的问题是:如果我们只在一个多线程进程中使用fprintffprintf 是线程安全的,即fprintf 的一个调用不会干预另一个线程中的另一个fprintf 调用?很多文章都这么说,但是我自己也不容易保证,所以在这里求助。

A:复制fd的代码是这样的:

......
rv = apr_file_dup2(stderr_log, s_main->error_log, stderr_p);//dup the stderr to the logfile
apr_file_close(s_main->error_log);//here ,2 fd point to the same file description,so close one of 

然后

B:apache 它自己使用这种方式进行日志记录:

......
if (rv != APR_SUCCESS) 
    ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s_main, ".........");

C:为了方便,我是这样登录的:

fprintf(stderr,".....\n")

我很确定 apache 和我使用相同的 fd 进行文件写入。

【问题讨论】:

最好贴出实际代码,而不是代码描述。 这是一个很长的程序,我的 fprintf 分散在各处。我会在 apache2 中找到将 stderr 复制到 error.log 的代码,然后将它们发布在这里 对不起,我的积分是不是因为这个原因被扣分了? 【参考方案1】:

如果您使用单个FILE 对象在打开的文件上执行输出,那么整个fprintf 调用该FILE 将是原子的,即锁定在FILE 上的持续时间fprintf 电话。由于FILE 是单个进程地址空间的本地地址,因此这种设置仅适用于多线程应用程序;它不适用于多进程设置,其中多个不同进程正在访问引用同一底层打开文件的单独 FILE 对象。即使您在这里使用fprintf,每个进程都有自己的FILE,它可以锁定和解锁而其他人看不到更改,因此写入最终可能是交错的。有几种方法可以防止这种情况发生:

    在共享内存中分配一个同步对象(例如进程共享的信号量或互斥锁),并使每个进程在写入文件之前获得锁(因此一次只能写入一个进程);或者

    使用文件系统级别的咨询锁定,例如fcntl 锁定或(非 POSIX)BSD flock 接口;或者

    不要直接写入日志文件,而是写入另一个进程将馈送到日志文件的管道。只要它们小于PIPE_BUF 字节长,就可以保证(通过 POSIX)对管道的写入是原子的。在这种情况下,您不能使用fprintf(因为它可能执行多个底层写入操作),但您可以使用snprintfPIPE_BUF 大小的缓冲区,后跟write

【讨论】:

一个管道只能有一个写端和一个读端。所以,一个管道只能在2个进程之间使用。如果我设置了一个专用的日志进程,然后超过2个工作进程,这些工作进程中的每一个都必须创建一个管道,然后通过该管道将日志消息传递给日志记录进程。所以,这些管道是相互独立的,即使没有 POSIX 的保证,这些管道中的内容也不会被其他工作进程交错。日志进程有责任保证它从许多管道收到的 msgs 不被交错- ----------我说得对吗?tks 不,您可以为所有这些管道使用相同的管道。 tks,我会试试这个:将管道写入端的 fd 复制到多个进程,并且日志记录进程保持读取端。 在使用分叉 apache 设置时如何在 apache 模块中使用 ap_log_error()?日志会使用这个函数交错,还是这个函数可以防止这种情况发生? 选项 #3 毫无意义。如果您首先使用snprintf 在缓冲区中合成您的消息,您可以直接在O_APPEND 文件描述符上使用write 系统调用。不需要管道或其他协调过程。

以上是关于多线程和多处理时 fprintf 的行为如何?的主要内容,如果未能解决你的问题,请参考以下文章

java 多线程问题 真的提高了效率吗?

多线程与多进程的比较

多线程和多进程分别是啥意思?

如何多线程(多进程)加速while循环(语言-python)?

linux 多线程信号处理总结

单线程多线程和多进程的效率对比实验