【问题标题】:C++ buffered stream IOC++ 缓冲流 IO
【发布时间】:2012-07-08 16:50:14
【问题描述】:

我了解默认情况下 C++ 支持的所有流 IO 都是缓冲的。

这意味着要输出的数据被放入缓冲区直到它满了,然后发送到输出设备,同样对于输入,一旦缓冲区为空,数据就会被读取......所有这些都是为了数字可以最大限度地减少昂贵的系统调用。

但是如何在行动中验证这种行为。我的意思是考虑以下代码

int main()
{
    cout << "Hello world\n";
    return 0
}

这里的缓冲在哪里出现?我知道有缓冲发生,但如何解释呢?输出会立即显示在屏幕上,那么有什么代码示例可以实际看到缓冲 I/O 的运行情况?

【问题讨论】:

  • 在cout的情况下,缓冲区是1个字节。当你读/写文件时,真正的缓冲就会发挥作用。
  • @Mr.Anubis 你是怎么理解的?不,这不一定是真的。
  • @KonradRudolph 好吧,我不记得这句话的来源,但大多数情况下都会发生这种情况
  • @Mr.Anubis 在标准流中没有使用 1 字节缓冲区的情况。 std::cout 正常缓冲。 (这与 C 中的 stdout 不同,它是行缓冲的@ 有效地模拟了它。)

标签: c++ iostream buffered


【解决方案1】:

首先,并非所有 iostream 都被缓冲;缓冲由附加的处理 streambuf。在filebuf 的情况下(由ifstreamofstream),输入将尽可能多地读取,最大为 缓冲区,并且输出将在溢出时刷新缓冲区,当 发生显式刷新或关闭,或者当对象被破坏时(其中 隐式调用 close)。

cout 的情况有点特殊,因为它永远不会被破坏或 关闭。系统保证flush 在调用exit 后至少调用一次(这就是发生的情况 当您从main 返回时)。这意味着之前的任何输出 从 main 返回将被刷新;如果你在使用 cout 静态对象的析构函数,您仍然需要显式刷新 当然。

也可以tie 一个输出流到一个输入流; cout 默认绑定到cin。在这种情况下,任何尝试从 绑定的流将刷新输出。

通常的约定是只使用std::endl 而不是简单地 输出'\n'std::endl 输出 '\n' 然后刷新 溪流。对于所有输出都出现非常重要的流 很快,可以设置一个unitbuf 标志,这意味着 流将在每个 &lt;&lt; 运算符的末尾刷新。 (std::cerr 默认设置了这个。)

最后,如果你想看看缓冲的效果,把类似 sleep(10) 在你的输出之后。如果它的输出立即出现,它已经 酡;如果不是,则它已被缓冲,并且发生了刷新 隐含在sleep 之后。

【讨论】:

  • 感谢您的回复。但是,如果您能给我提供一个始终有效的代码示例,那就太好了。
  • @Arun 哪个始终如一地工作?使用 std::endl 而不是 '\n' 将导致在每行末尾刷新。如果您需要更多,std::cout &lt;&lt; std::flush 将无条件刷新输出。
  • 我需要一个描述缓冲区作用的例子。类似 cout
  • @Arun 所以在这两个语句之间放置一个延迟,您应该会看到直到延迟之后才会出现任何输出。
  • 在 VS2008 和 VS2010 上尝试过,但没有成功。语句立即输出到屏幕上。因此没有观察到缓冲的证据。
【解决方案2】:

试试下面的程序。 sleep(1) 用于引入延迟(1 秒),我使用的是 linux,所以 sleep 适合我。如果你不能让它工作,尝试其他方式来延迟这个程序(例如,简单的for循环)。如果您没有看到任何缓冲效果,您也可以尝试增加缓冲区大小(取消注释代码行)。

在我的操作系统(Linux 3.2.0)和编译器(g++ 4.6.3)上,这个程序打印“Portion1Portion2”,然后是“Portion3Portion4”,然后是“Portion5”。 std::endl guaranteed to flush 缓冲区,但正如您所见,换行符也适用于我。

#include <iostream>
#include <unistd.h>

using namespace std;

int main () {
    // Try uncommenting following lines to increase buffer size
    // char mybuf[1024];
    // cout.rdbuf()->pubsetbuf(mybuf, 1024);

    cout << "Portion1";
    sleep(1);
    cout << "Portion2\n";
    sleep(1);
    cout << "Portion3";
    sleep(1);
    cout << "Portion4" << endl;
    sleep(1);
    cout << "Portion5" << endl;
    sleep(1);
    cout << "Done!" << endl;

    return 0;
}

【讨论】:

  • 即使没有注释行?
【解决方案3】:

试试下面的代码:

int main()
{
    for( int i =0 ; i < 10; i ++ )
    {
        cout << i << " ";
        cerr << i << " ";
    }
}

缓冲的输出通常会随着流对象的销毁而被刷新,因此上面的代码会打印出来(并非总是如此,ofc,但它在 gcc 4.6.3 中对我有用)

0 1 2 3..9
0 1 2 3..9

而不是

0 0 1 1 2 2 3 3 .... 9 9 

因为未缓冲的cerr 会立即打印(第一个序列),而缓冲的cout 会在main() 的末尾打印。

【讨论】:

  • @Arun,这就是我说“不总是”的原因。缓冲是特定于实现的,因此您将看到使用不同编译器和设置的不同结果。有时输出甚至会以奇怪的顺序混合。
  • std::cout 永远不会被破坏,但可以出于其他原因刷新缓冲区。在这种情况下,std::cinstd::cerr 都绑定到std::cout,这意味着其中一个上的任何 IO 都会对std::cout 产生干扰。而std::cerr 是单元缓冲的,这意味着每个&lt;&lt; 运算符都会在操作结束时刷新它。
  • @SingerOfTheFall 没有始终如一的例子吗?我尝试了问题中的所有示例,都没有工作...这是否意味着 VS2010,它总是刷新缓冲区?
  • @Arun,对不起,我不知道一个一致的例子。我已经发布了我的输出图像,请检查答案。
  • @SingerOfTheFall 它可能(可能)取决于库的版本。 C++11 要求将std::cout 绑定到std::cerr(除了std::cin);这意味着std::cerr 的任何输出都将刷新std::cout,给出“0 0 1 1 ...”。 C++03 不允许绑定,所以通常会给出“0 1 2... 0 1 2...”。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-09-12
  • 2010-11-29
  • 2012-01-19
  • 2011-11-14
  • 1970-01-01
  • 2023-03-16
相关资源
最近更新 更多