关于fstream缓冲区,flush()和sync()有什么区别?

15

我正在阅读cplusplus.com I/O教程,该教程的结尾提到fstream缓冲区与磁盘上的文件同步。

显式地使用操纵符: 当在流上使用某些操纵符时,会发生显式同步。这些操纵符包括: flush 和 endl。

以及

显式地使用成员函数sync(): 调用流的成员函数sync(),不带参数,会立即进行同步。如果流没有关联的缓冲区或出现故障,则此函数返回一个等于-1的int值。否则(如果流缓冲区成功同步),它将返回0。

除了一些隐式情况外(例如销毁和stream.close()),还有其他几种情况。

调用fstream::flush()、fstream::sync()和endl之间有什么区别?

在我的代码中,我总是使用flush()。

有关std::flush()的文档:

刷新流缓冲区 将与流相关联的缓冲区与其受控输出序列同步。这实际上意味着缓冲区中所有未写入的字符都尽可能快地被写入其受控输出序列("刷新")。

std::streambuf::sync() 的文档:

将输入缓冲区与字符源同步 调用此函数以将流缓冲区与受控序列(例如文件流中的文件)同步。公共成员函数 pubsync 调用此保护成员函数执行此操作。

如果这是一个新手问题,请原谅;我是个新手。

5个回答

17

basic_ostream::flush 这是一个非虚拟函数,它将未提交的更改写入底层缓冲区。如果发生错误,则在使用的流对象中设置错误标志。这是因为返回值是指向流本身的引用,以允许链接。

basic_filebuf::sync 这是一个虚拟函数,它将所有挂起的更改写入底层文件,并返回一个错误代码,以表示成功或失败。

endl 当应用于ostream时,它会向流中写入'\n',然后调用该流上的flush

因此,本质上:flush是任何流的更通用的函数,而sync明确绑定到文件。 flush是非虚拟的,而sync是虚拟的。这会影响它们在继承情况下如何通过指针(到基类)使用。此外,它们不同的是它们报告错误的方式。


1
非常好的回答。您能否确认/澄清DWright和perreal关于flush()由内核处理,而sync()由库处理(当内核无法快速调度写操作时)的说法? - cjcurrie
2
@cjcurrie 写入文件时,内核总是参与其中,但在写入流时不一定涉及内核,因为流可能绑定到完全由库处理的内容(例如stringstream)。 - Agentlien
这是错误的,sync()函数不会写入任何内容。它用于与输入数据同步,而不是输出。 - Alexis Wilke
@doc 确实,basic_filebuf 基于 basic_streambuf,后者具有一个 sync 函数,除非被覆盖,否则不会执行任何操作,并且不特定于文件。另一方面,basic_filebuf 对此函数的覆盖是针对文件特别定义的。 - Agentlien
2
我认为区别在于flush()与输出流相关,而sync()与输出缓冲区相关。flush()基本上调用底层缓冲区的sync(),无论是文件缓冲区还是其他任何缓冲区。OP询问fstream::flush()fstream::sync()之间的区别,而您描述的是basic_ostreambasic_filebuf之间的区别,所以我不明白这里发生了什么。 - mip
显示剩余3条评论

5

syncinput 流的成员函数,它会清空缓冲区中所有未读字符。 flushoutput 流的成员函数,它会将缓冲的输出传递到内核。


4
C++的输入输出涉及多个类之间的协作:streambufferlocalelocale::facet。在特定情况下,syncflushstreamstreambuf中均存在的成员函数,因此请注意引用的文档,因为它们执行不同的操作。
对于streamsflush告诉流将缓冲区(注意重定向)的内容刷新到目标上。这可以确保没有“挂起的写入”留下。
std::endl应用于<<thestream时,其实就是一个换行符,使缓冲区被刷新到目标。
thestream.put('\n'); thestream.flush();

始终开启 同步 告诉 告诉 缓冲区 刷新内容(对于输出)并尽可能地读取(对于输入)以重新填充缓冲区。
请注意,在 缓冲区 内,sync 也可以通过 溢出 在内部调用来处理“缓冲区已满”(对于输出)和“缓冲区为空”(对于输入)的情况。
因此,我认为,同步 更多地是用于流到缓冲区通信和缓冲区实现的“内部”功能(在不同的缓冲区类型中是虚拟的并被覆盖),而 刷新 更多地是流与客户端程序之间的接口。 endl ... 只是一个快捷方式。

什么是On STREAM和Always on STREAM之间的区别?恐怕我不明白你在这里的意思。 - cjcurrie
我谈到了流、缓冲区和其他东西(本地化等)。当说“在流上”时,我只是指“而不是在缓冲区上”。如果你喜欢,可以用“使用”代替“在”。 - Emilio Garavaglia

0
调用 `fstream::flush()` 和 `fstream::sync()` 有什么区别?没有区别:两者本质上都调用 `rdbuf()->pubsync()`,然后调用`std::streambuf::sync()`,请参见 https://en.cppreference.com/w/cpp/io/basic_fstream 中的链接。在构造和检查 sentry 对象后,调用 `rdbuf()->pubsync()`,并且 https://en.cppreference.com/w/cpp/io/basic_streambuf/pubsync 中调用最终派生类的 `sync()`。
唯一的区别在于函数定义的位置:sync继承自istream,而flush继承自ostreamfstream同时继承两者)。当然,返回值也不同:sync返回一个int(0表示正常,-1表示失败),而flush返回流对象的引用。但你可能并不关心这些。
输入和输出流的命名差异在于对于输入,它会将内部缓冲区与输入流(这里是文件)“同步”,以防发生更改,而对于输出,则会将待处理的更改从内部缓冲区刷新到输出流(同样是文件)。即“从同步”和“向刷新”在命名上更有意义。但对于一个iostream而言。
为了完整起见(几乎),来自Emilios答案的内容:

std::endl,当应用于<<thestream时,不过是一个
thestream.put('\n').flush();

所以它添加了一个换行符,然后调用流的flush函数,该函数最终通过pubsync调用缓冲区的sync函数。
这只是一种使用行缓冲的快捷方式,即先写入(最多)到换行符的末尾,然后刷新已写入的内容。

0

我理解如下:

flush() 将数据从库缓冲区中取出并放入操作系统的写入缓冲区,最终会导致完全同步(数据完全写出),但是同步完成的时间取决于操作系统。

sync() 将尽可能地在给定的操作系统中尝试强制进行完全同步,但涉及的操作系统可能会或可能不会促进此过程。

因此,flush() 是:将数据从缓冲区中取出并排队等待写入。
sync() 是:如果可能,立即强制数据被明确写出。

这就是我的理解,但是当我思考它时,我无法记得我是如何理解的,所以我也很想听听其他人的看法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接