在c ++中刷新流的后果和优缺点

The*_*Fan 25 c++ theory flush

我最近阅读了一篇文章,其中指出使用比使用\n更好,std::endl因为它endl也会刷新流.
但当我找到关于该主题的更多信息时,我发现了一个网站,其中说明:

如果您处于必须避免缓冲的情况,可以使用std :: endl而不是'\n'

现在我的问题出现了:在哪种情况下最好不要写入缓冲区?因为我只看到了那种技术的优点.写入缓冲区不是更安全吗?因为它比硬盘驱动器小,所以它会比存储在HD上的数据更快地被覆盖(我不确定这是否属实).

Mar*_* A. 19

发生缓冲时,您无法保证在刷新发生之前立即接收数据.在特定情况下,您可能会遇到错误的输出排序和/或信息/调试数据丢失,例如

int main() {

    std::cout << "This text is quite nice and might as well be buffered";
    raise(SIGSEGV); // Oh dear.. segmentation violation
    std::cout << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

Live Example

输出:

bash: line 7: 22235 Segmentation fault      (core dumped) ./a.out
Run Code Online (Sandbox Code Playgroud)

由于缓冲阻止了正确的输出显示,因此上面不会打印任何文本.

现在,如果你只是std::endl在缓冲区的末尾添加一个刷新,这就是你得到的

int main() {

    std::cout << "This text is quite nice and might as well be buffered" << std::endl;
    raise(SIGSEGV); // Oh dear.. segmentation violation
    std::cout << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

Live Example

输出:

This text is quite nice and might as well be buffered
bash: line 7: 22444 Segmentation fault      (core dumped) ./a.out
Run Code Online (Sandbox Code Playgroud)

这次输出在程序终止之前可见.

这一事实的含义是多方面的.纯粹推测:如果数据与服务器日志相关,那么您的应用程序可能在实际日志记录之前崩溃了.


Pet*_*etr 11

在任何你希望输出实际出现在应该出现的时候,它会更好.

一个简单的例子:

#include <iostream>
int main() {
    std::cout << "Please enter your name: " << std::endl;
    std::string name;
    std::cin >> name;
    ...
}
Run Code Online (Sandbox Code Playgroud)

通过缓冲,在用户需要输入他/她的名字之前,屏幕上不会出现任何文本,因此用户会感到困惑.(请注意,事实上,在完全启用缓冲的情况下运行此示例可能非常困难或不可能,因为C++可能需要特殊措施std::cout在输入之前进行刷新std::cin,请参阅为什么我们需要绑定std :: cin和std ::清点?但是,这仅仅是一个理论上的例子:如果缓存.完全启用,用户将不会看到提示)

这种情况可能会不时发生,尽管可能并非经常发生.考虑写入管道以与另一个进程交互.或者即使您的程序写入日志文件,您也会不时查看日志文件以查看它是如何运行的 - 如果是缓冲,您通常不会看到从程序打印的输出,但仍然留在缓冲区了.

要考虑的另一个重要情况 - 如果您的程序严重崩溃,缓冲区内容可能根本不会在硬盘驱动器上结束.(我希望流析构函数能够刷新缓冲区,但是崩溃可能非常严重,根本不会调用析构函数.)

  • @Petr你的例子忽略了`std :: cin`和`std :: cout`绑定的事实,这样`std :: cin`上的任何输入操作都会首先导致`std :: cout`的输出缓冲区脸红了 例如,请参见http://stackoverflow.com/questions/14052627/why-do-we-need-to-tie-cin-and-cout. (8认同)
  • 安德鲁绝对正确 - 这个答案需要更新.更好地说明刷新有用的例子是`for(int i = 0; i <10; ++ i){std :: cout <<"\ rplease wait"<< i <<"更多秒...... "<< std :: flush; 睡眠(1); }`. (8认同)
  • @TheJavaFan如果你的意思是"将缓冲使事情变得更快"答案是肯定的,这就是缓冲的全部目的.无论如何[任​​何时候输出时间很重要](http://stackoverflow.com/a/29701830/1938163)你应该考虑利弊.这完全是针对特定应用的. (2认同)

Dre*_*ann 10

如果您需要流的目标在流关闭之前接收数据,则最好刷新缓冲区.

一个真实的例子是应用程序日志,从始终打开的流写入...您可能希望在程序仍在运行时查看此日志.


小智 10

首先,有点修正主义的历史.

在过去,当每个人都使用stdio.h库来进行I/O时,交互式查看的文本通常是行缓冲的(甚至是无缓冲的),而未完全缓冲的文本也是如此.因此,如果您输出'\n'到流,它将"始终"执行正确的事情:用户正在查看的行被刷新并立即被看到,并且被转储到文件的行被缓冲以获得最大性能.

不幸的是,它实际上并不总是正确的事情; 运行时无法始终预测用户实际想要查看程序输出的方式.一个常见的陷阱是重定向STDOUT- 人们习惯于在控制台中运行程序并在控制台中查看输出(带有行缓冲行为),然后出于任何原因(例如长时间运行的作业),他们决定重定向STDOUT到文件,并立即惊讶于输出不再是行缓冲.

我已经看到因为这个原因浪费了数周的超级计算机时间; 输出很少,缓冲阻止任何人能够告诉工作进展情况.

iostream然而,C++的库旨在让你在这里轻松做正确的事情.除了与stdio它同步时,它不会做这个有趣的"可能行缓冲可能完全缓冲"的事情.它总是使用完全缓冲(当然,当你做无缓冲的东西时除外),如果你想在换行上刷新东西,你可以明确地这样做.

因此,如果您将一堆格式化文本转储到人们在完成之前不会查看的文件中,那么您可以\n为换行符编写代码.

但是如果你正在写文字,人们可能真的想在你写作的时候看一下,你会用std::endl你的换行符,它会立即显示出来.如果你一次写几行,你甚至可以做得更好:'\n'用于中间换行符和std::endl最后一行(或'\n'std::flush).虽然在这个设置中性能通常无关紧要,但通常可以使用std::endl所有的换行符.


Pet*_*ker 5

我希望你丢失了你找到的那个网站的链接.std::endl没有避免缓冲.它刷新缓冲区中的任何内容.如果您需要避免缓冲,请使用setf(ios_base::unitbuf).这会在每次插入后将流设置为刷新.这是默认设置std::clog.这样做的原因是缓冲区中保存的内容越少,关键数据在程序崩溃时写入流的可能性就越大.

刷新对于交互式程序也很重要:如果你写了一个提示std::cout,如果在程序开始等待输入之前该提示出现在显示器上,这是一件好事.当您使用这就是自动完成std::coutstd::cin,除非你与同步设置弄乱.

许多程序员似乎都使用std::endl拼写的方式'\n',但事实并非如此.每次向其写入内容时都不需要刷新输出缓冲区.让操作系统和标准库完成他们的工作; 他们会及时将产量输送到适当的地方.std::cout << '\n';只需简单就可以将换行符放入输出中,迟早会出现在显示屏上.如果您现在需要显示它,通常是因为您暂时写了所有输出并且不想让显示的信息不完整,请std::endl在输出的最后一行之后使用.