如何在内存中缓冲 stdout 并从专用线程写入它
我有一个带有许多工作线程的 C 应用程序。 重要的是,这些不会阻塞,因此当工作线程需要写入磁盘上的文件时,我让它们写入内存中的循环缓冲区,然后有一个专用线程将该缓冲区写入磁盘。
工作线程不再阻塞。 专用线程在写入磁盘时可以安全地阻塞,而不会影响工作线程(写入磁盘时它不持有锁)。 我的内存缓冲区已调整为足够大,以便编写器线程可以跟上。
这一切都很好。 我的问题是,如何为标准输出实现类似的东西?
我可以宏 printf() 写入内存缓冲区,但我无法控制可能写入标准输出的所有代码(其中一些位于第三方库中)。
想法? 尼克B
I have a C application with many worker threads. It is essential that these do not block so where the worker threads need to write to a file on disk, I have them write to a circular buffer in memory, and then have a dedicated thread for writing that buffer to disk.
The worker threads do not block any more. The dedicated thread can safely block while writing to disk without affecting the worker threads (it does not hold a lock while writing to disk). My memory buffer is tuned to be sufficiently large that the writer thread can keep up.
This all works great. My question is, how do I implement something similar for stdout?
I could macro printf() to write into a memory buffer, but I don't have control over all the code that might write to stdout (some of it is in third-party libraries).
Thoughts?
NickB
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
我喜欢使用
freopen
的想法。 您还可以使用 dup< 将stdout
重定向到管道/a> 和 dup2,然后使用read
从管道中获取数据。像这样:
I like the idea of using
freopen
. You might also be able to redirectstdout
to a pipe using dup and dup2, and then useread
to grab data from the pipe.Something like so:
如果您使用 GNU libc,则可以使用
内存流字符串流 。If you're working with the GNU libc, you might use
memory streamsstring streams.您可以使用
freopen()
将stdout
“重定向”到文件中。man freopen
说:该文件很可能是一个管道 - 工作线程将写入该管道,而写入线程将侦听。
You can "redirect"
stdout
into file usingfreopen()
.man freopen
says:This file well could be a pipe - worker threads will write to that pipe and writer thread will listen.
为什么不将整个应用程序包装在另一个应用程序中呢? 基本上,您想要的是一个智能
cat
,它将标准输入复制到标准输出,并根据需要进行缓冲。 然后使用标准 stdin/stdout 重定向。 这可以在根本不修改当前应用程序的情况下完成。Why don't you wrap your entire application in another? Basically, what you want is a smart
cat
that copies stdin to stdout, buffering as necessary. Then use standard stdin/stdout redirection. This can be done without modifying your current application at all.您可以使用
setvbuf()
或setbuf()
更改缓冲的工作方式。 这里有一个描述: http://publications.gbdirect.co.uk/c_book /chapter9/input_and_output.html。[编辑]
stdout
实际上是一个FILE*
。 如果现有代码可以与FILE*
一起使用,我看不出是什么阻止它与stdout
一起使用。You can change how buffering works with
setvbuf()
orsetbuf()
. There's a description here: http://publications.gbdirect.co.uk/c_book/chapter9/input_and_output.html.[Edit]
stdout
really is aFILE*
. If the existing code works withFILE*
s, I don't see what prevents it from working withstdout
.一种解决方案(对于您所做的这两件事)是通过 writev 使用聚集写入。
例如,每个线程可以将 sprintf 放入 iovec 缓冲区,然后将 iovec 指针传递给编写器线程,并让它简单地使用 stdout 调用 writev。
这是使用 writev 的示例,来自 高级 Unix 编程
在 Windows 下,您将使用WSAsend 具有类似的功能。
One solution ( for both things your doing ) would be to use a gathering write via writev.
Each thread could for example sprintf into a iovec buffer and then pass the iovec pointers to the writer thread and have it simply call writev with stdout.
Here is an example of using writev from Advanced Unix Programming
Under Windows you would use WSAsend for similar functionality.
使用 4096 bigbuf 的方法只能起到一定的作用。 我已经尝试过这段代码,虽然它确实成功地将标准输出捕获到缓冲区中,但它在现实情况下无法使用。 您无法知道捕获的输出有多长,因此无法知道何时终止字符串 '\0'。 如果您尝试使用缓冲区,并且已成功捕获 96 个字符的 stdout 输出,则会输出 4000 个垃圾字符。
在我的应用程序中,我在 C 程序中使用 perl 解释器。 我不知道在 C 程序中抛出的任何文档会产生多少输出,因此上面的代码永远不允许我在任何地方干净地打印该输出。
The method using the 4096 bigbuf will only sort of work. I've tried this code, and while it does successfully capture stdout into the buffer, it's unusable in a real world case. You have no way of knowing how long the captured output is, so no way of knowing when to terminate the string '\0'. If you try to use the buffer you get 4000 characters of garbage spit out if you had successfully captured 96 characters of stdout output.
In my application, I'm using a perl interpreter in the C program. I have no idea how much output is going to be spit out of what ever document is thrown at the C program, and hence the code above would never allow me to cleanly print that output out anywhere.