2012-06-22 43 views
2

我的代码看起来接近该:竞争条件

  1. 大量使用普通的C++ IO流插入的写操作的像steram << "foo";
  2. stream.flush();在一些点;
  3. MSVC C API的_stat()在上面刷新后调用。

我所观察到的是,_stat()调用返回的大小从stream.tellp()不同,尺寸较小。

如果我在调用_stat()函数之前关闭stream,它会返回正确的结果。我已经步入_stat(),它使用FindFirstFileEx()来获得大小。

这是一个已知的Win32 API quirk吗?

+2

是否有可能stream.flush()是异步的,并在缓冲区刷新之前返回? –

+0

您应该通过FileMon检查,哪些操作是由您的呼叫执行的。如果偶然使用FASTIO_WRITE而不是IRP_MJ_WRITE,那么您的代码将隐式使用FASTIO的中间缓冲区。也许你可以通过编程实现对IRP的回退,但这会减慢写入过程。 – Stan

+3

这是为NTFS文件系统而设计的,而不是api怪癖。在关闭文件的所有句柄之前,目录记录中的属性不反映实际的文件属性。不仅仅是文件大小,还有时间戳值。 –

回答

2

我假设您正在使用Windows Vista或更高版本。每次将文件刷新到光盘时,文件大小在XP下的文件属性中都会更新。在Vista上,这不再是正确的,因为汉斯已经评论过文件属性,并且在关闭文件的最后一个句柄时更新了它的大小。

查看Old New Thing Blog了解更多详情。

您可以尝试第二次打开文件并关闭它。

+0

感谢您访问博客的链接。它解释了很多事情。尽管在我看来这是'_stat()'中的一个错误,那么它使用的函数可能会让我失去数据。 – wilx

+0

这不是唯一的怪癖。 Windows具有称为文件系统隧道的功能,它在相当长的时间内报告相同的文件大小。例如,如果你是登录到文件,关闭,重命名并再次打开具有相同名称的文件。见http://blogs.msdn.com/b/oldnewthing/archive/2005/07/15/439261.aspx –

+0

呃。这看起来像无理取悦懒惰或糟糕的编码者。如果我希望文件具有相同的时间戳,我会自己处理。 – wilx

0

这可能是由于文件系统缓存。从C++流的角度来看,它已按照标准的要求将所有写入数据传递到物理设备。但文件系统可能会缓存写入,并根据其他策略刷新它们。

一个可能的解决办法是在以下Win32 API的选项使用:

  1. 如果你碰巧自己打开文件,使用CreateFile,使用FILE_FLAG_NO_BUFFERINGFILE_FLAG_WRITE_THROUGH标志。
  2. 编写完成后,请致电FlushFileBuffers以确保元数据已刷新(您必须以某种方式获取该文件的句柄)。

这应该不会损害性能太多,假设您的C++流正在做自己的缓冲。

+0

系统不应该确保其内部数据结构之间的一致性吗?我实际上重做了代码,以便它在'_stat()'调用之前执行'stream.close()',现在它可以工作。 – wilx

+0

@wilx,一致性是有代价的。如果不确保一致性,高速缓存运行得更好,因此您应该对自己的应用程序说哪一个更重要。我很高兴你解决了你的问题,看看我的建议是否能解决问题也会很有趣。 – eran

+0

当然,一致性和正确性比速度更重要。 – wilx

1

使用GetFileInformationByHandle获取确切的大小。