C 如何在内存中缓冲标准输出并从专用线程写入
我有一个具有许多工作线程的C应用程序。重要的是,这些线程不会阻塞,因此当工作线程需要写入磁盘上的文件时,我让它们写入内存中的循环缓冲区,然后有一个专用线程将该缓冲区写入磁盘 工作线程不再阻塞。专用线程在写入磁盘时可以安全地阻塞,而不会影响工作线程(在写入磁盘时它不会持有锁)。我的内存缓冲区被调整为足够大,写入线程可以跟上 这一切都很好。我的问题是,如何为stdout实现类似的东西 我可以通过宏printf()写入内存缓冲区,但我无法控制可能写入stdout的所有代码(其中一些代码位于第三方库中) 想法? NickB您可以使用C 如何在内存中缓冲标准输出并从专用线程写入,c,file,stdout,buffering,C,File,Stdout,Buffering,我有一个具有许多工作线程的C应用程序。重要的是,这些线程不会阻塞,因此当工作线程需要写入磁盘上的文件时,我让它们写入内存中的循环缓冲区,然后有一个专用线程将该缓冲区写入磁盘 工作线程不再阻塞。专用线程在写入磁盘时可以安全地阻塞,而不会影响工作线程(在写入磁盘时它不会持有锁)。我的内存缓冲区被调整为足够大,写入线程可以跟上 这一切都很好。我的问题是,如何为stdout实现类似的东西 我可以通过宏printf()写入内存缓冲区,但我无法控制可能写入stdout的所有代码(其中一些代码位于第三方库中)
freopen()
将stdout
重定向到文件中
man freopen
说:
函数的作用是:打开文件
字符串指向谁的名字
通过路径并关联流
用它指着溪流。这个
原始流(如果存在)为
关闭使用mode参数
就像在fopen()函数中一样。
freopen()的主要用途
函数用于更改文件
与标准文本关联
流(stderr、stdin或stdout)
此文件很可能是管道-工作线程将写入该管道,写入线程将侦听。您可以使用
setvbuf()
或setbuf()
更改缓冲的工作方式。这里有一个描述:
[编辑]
stdout
实际上是一个文件*
。如果现有代码与文件*
一起工作,我看不出是什么阻止它与标准输出一起工作
为什么不将整个应用程序包装到另一个文件中?基本上,您需要的是一个智能的cat
,它将stdin复制到stdout,并根据需要进行缓冲。然后使用标准stdin/stdout重定向。这可以在不修改当前应用程序的情况下完成
~MSalters/# YourCurrentApp | bufcat
如果您正在使用GNULIBC,您可能会使用。一个解决方案(对于您所做的两件事)是使用一个收集写通孔 例如,每个线程都可以将sprintf放入iovec缓冲区,然后将iovec指针传递给writer线程,让它用stdout简单地调用writev 下面是一个使用writev from的示例
在Windows下,您可以使用WSAsend实现类似的功能。我喜欢使用
freopen
的想法。您还可以使用and将stdout
重定向到管道,然后使用read
从管道中获取数据
大概是这样的:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define MAX_LEN 40
int main( int argc, char *argv[] ) {
char buffer[MAX_LEN+1] = {0};
int out_pipe[2];
int saved_stdout;
saved_stdout = dup(STDOUT_FILENO); /* save stdout for display later */
if( pipe(out_pipe) != 0 ) { /* make a pipe */
exit(1);
}
dup2(out_pipe[1], STDOUT_FILENO); /* redirect stdout to the pipe */
close(out_pipe[1]);
/* anything sent to printf should now go down the pipe */
printf("ceci n'est pas une pipe");
fflush(stdout);
read(out_pipe[0], buffer, MAX_LEN); /* read from pipe into buffer */
dup2(saved_stdout, STDOUT_FILENO); /* reconnect stdout for testing */
printf("read: %s\n", buffer);
return 0;
}
#包括
#包括
#包括
#定义最大长度40
int main(int argc,char*argv[]){
字符缓冲区[MAX_LEN+1]={0};
int out_管道[2];
int保存的标准输出;
saved_stdout=dup(stdout_FILENO);/*保存stdout供以后显示*/
如果(管道)!=0{/*制作管道*/
出口(1);
}
dup2(输出管道[1],标准输出文件号);/*将标准输出重定向到管道*/
关闭(管道[1]);
/*发送到printf的任何内容现在都应该被传送到管道中*/
printf(“ceci n'est pas une管道”);
fflush(stdout);
读取(输出管道[0],缓冲区,最大长度);/*从管道读取到缓冲区*/
dup2(已保存的标准输出,标准输出文件号);/*重新连接标准输出以进行测试*/
printf(“读取:%s\n”,缓冲区);
返回0;
}
使用4096 bigbuf的方法只会起到某种作用。我尝试过这段代码,虽然它成功地将stdout捕获到缓冲区中,但在现实世界中它是不可用的。您无法知道捕获的输出有多长,因此无法知道何时终止字符串“\0”。如果您尝试使用该缓冲区,那么如果您成功捕获了96个字符的标准输出,您将得到4000个字符的垃圾
在我的应用程序中,我在C程序中使用perl解释器。我不知道在C程序中抛出的文档会有多少输出,因此上面的代码不允许我在任何地方清晰地打印输出。这并不能解决我的问题。我正在尝试将磁盘从执行printf()调用的线程中移出。使用freopen()仍会使printf()调用写入文件,尽管与stdout的文件不同。我可以为freopen()指定一个不是磁盘文件的“文件”吗?当然可以。使用管道而不是文件。这听起来好像解决了我的问题。我试试看。非常感谢。当我尝试这种方法时,我发现如果管道中没有任何内容,则
read
会挂起。但是添加long flags=fcntl(out_pipe[0],F_GETFL);标志|=O|U非块;fcntl(输出管道[0],F\U设置FL,标志)代码>到初始化部分修复了这个问题。正是我要找的!很好的方法,但是如何在Windows中使用Similar?@NateKohl Hey Nate,很棒的代码,你能为两个不同的函数提供一个例子吗?一个读取另一个的输出,另一个读取另一个的输出。这里所有的解决方案都是错误的,因为写入管道会阻塞。(如果您将它们设置为非阻塞,那么文件
将进入不可恢复的错误状态,如果它在需要刷新时阻塞。)我认为这是真正正确的答案。管道方法涉及昂贵的系统调用,肯定会阻塞。链接已过期,请改用此方法。如果先用零填充bigbuf,则可以保证字符串将以null结尾。。。除非bigbuf的最后一个字节不是零。但在这种情况下,您可以检测到溢出。