【问题标题】:Why is stdout buffering?为什么标准输出缓冲?
【发布时间】:2016-10-25 18:11:39
【问题描述】:

我正在尝试学习libuv api并编写了以下测试:

#include <stdio.h>
#include <stdlib.h>
#include <uv.h>

void timer_cb(uv_timer_t* timer) {
    int* i = timer->data;
    --*i;
    if(*i == 0) {
       uv_timer_stop(timer);
    }
    printf("timer %d\n", *i);
    //fflush(stdout);
}

int main() {
    uv_loop_t* loop = uv_default_loop();
    uv_timer_t* timer = malloc(sizeof(uv_timer_t));
    uv_timer_init(loop, timer);
    int i = 5;
    timer->data = &i;
    uv_timer_start(timer, timer_cb, 1000, 2000);

    uv_run(loop, UV_RUN_DEFAULT);

    printf("Now quitting.\n");
    uv_close(timer, 0);
    uv_loop_close(loop);

    return 0;
}

运行时不显示输出,直到程序运行完毕,然后一次性显示所有输出。如果我取消注释 fflush 行,它会按预期工作,每 2 秒写入一次。

有人可以向我解释一下吗?为什么stdout 在换行符之后没有刷新,正如here 和其他地方所解释的那样?为什么需要手动冲洗?

【问题讨论】:

  • “为什么 stdout 缓冲” - 为什么不呢?
  • 这个答案可能对你有用:stackoverflow.com/a/5229135/868691
  • @AndrewHenle Windows,在 msys 上使用 mingw64
  • @Olaf 我了解(请参阅最后链接到的答案)缓冲区刷新换行符
  • @baruch:你在哪里找到标准中的

标签: c libuv


【解决方案1】:

流缓冲是实现定义的。

根据 7.21.3 文件C Standard 的第 3 段:

当一个流是 无缓冲,字符旨在尽快从源或目标出现。否则字符 可以累积并传输到主机或从主机传输 环境作为一个块。当一个流是 完全缓冲,当缓冲区填满时,字符将作为块传输到主机环境或从主机环境传输。当一个 流是 行缓冲,当换行符时,字符旨在作为块传输到主机环境或从主机环境传输 遇到字符。此外,字符旨在 当一个缓冲区被作为一个块传输到主机环境 填充,当在无缓冲流上请求输入时,或者当 在需要的行缓冲流上请求输入 从主机传输字符 环境。 对这些特性的支持是 实现定义,并可能通过setbufsetvbuf 函数。

缓冲的类型取决于您的实现,在您的示例中,您的实现显然不是行缓冲。

【讨论】:

  • 我用setvbuf(stdout, NULL, _IOLBF, 0); 将它设置为行缓冲,但它仍然不起作用。只有当我将其设置为无缓冲 (_IONBF) 时它才能工作
  • 正如该答案中所链接的,在 Windows 上没有行缓冲。我认为这是问题msdn.microsoft.com/en-us/library/86cebhfs.aspx#Anchor_3
【解决方案2】:

没有严格的要求,stdout 是行缓冲的。它也可能被完全缓冲(或根本没有缓冲),在这种情况下\n 不会触发刷新流。

C11 (N1570) 7.21.3/7 文件

最初打开时,标准错误流没有完全缓冲; 标准输入和标准输出流是完全缓冲的,如果 并且仅当可以确定流不引用 交互式设备。

C11 (N1570) 5.1.2.3/7 程序执行

什么构成交互式设备是由实现定义的。

您可以尝试通过setvbuf 标准函数强制执行特定类型的缓冲。例如,要为stdout 设置行缓冲,您可以尝试:

setvbuf(stdout, buff, _IOLBF, size);

其中buff 被声明为size 元素的字符数组(例如1024)。

请注意,setvbuf 必须在任何其他 I/O 操作之前调用,即对流执行。

【讨论】:

  • 我用setvbuf(stdout, NULL, _IOLBF, 0); 将它设置为行缓冲,但它仍然不起作用。只有当我将其设置为无缓冲 (_IONBF) 时它才能工作
  • @baruch: AFAIR,setvbuf 需要为它提供自己准备的缓冲区。否则,它将不起作用。
【解决方案3】:

由于某种原因,您的系统决定您的标准输出不是交互式的。你是在做一些奇怪的标准输出重定向还是在用你的终端做一些奇怪的事情?您应该能够使用 setbuf 覆盖,或者您可以使用 stderr 而不是 stdout。

【讨论】:

  • 这没有提供问题的答案。要批评或要求作者澄清,请在他们的帖子下方留下评论。 - From Review
  • 我认为它包括 2 个答案:使用 setvbuf 或 stderr。第三个答案只是隐含的,以弄清楚为什么系统将 stdout 误认为是非交互式的。
猜你喜欢
  • 1970-01-01
  • 2013-11-28
  • 2013-10-27
  • 1970-01-01
  • 2014-12-07
  • 2011-08-20
  • 2012-07-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多