【问题标题】:race condition between C++ stream's flush() and FindFirstFileEx()C++ 流的 flush() 和 FindFirstFileEx() 之间的竞争条件
【发布时间】:2012-06-24 17:04:48
【问题描述】:

我的代码看起来接近这个:

  1. 大量写入使用普通 C++ IO 流插入,例如 steram << "foo";
  2. stream.flush(); 在某个时候;
  3. MSVC C API 的 _stat() 在上述刷新之后立即调用。

我观察到的是_stat() 调用返回的尺寸与stream.tellp() 不同,尺寸更小。

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

这是一个已知的 Win32 API 怪癖吗?

【问题讨论】:

  • stream.flush() 是否有可能是异步的,在缓冲区刷新之前返回?
  • 您应该通过 FileMon 检查您的调用执行哪些操作。如果他们不小心使用了 FASTIO_WRITE 而不是 IRP_MJ_WRITE,那么您的代码会隐式使用 FASTIO 的中间缓冲区。也许您可以通过编程方式强制回退到 IRP,但这会减慢编写过程。
  • 这是为 NTFS 文件系统设计的,而不是 api 怪癖。在关闭文件的所有句柄之前,目录记录中的属性不会反映实际的文件属性。不仅仅是文件大小,还有时间戳值。
  • @HansPassant:如果我使用GetFileAttributesEx() 而不是_stat(),我会得到更准确的信息吗?

标签: c++ winapi visual-c++ iostream


【解决方案1】:

我假设您正在使用 Windows Vista 或更高版本。每次将文件刷新到光盘时,都会在 XP 下的文件属性中更新文件大小。这在 Vista 上不再适用,因为 Hans 已经对文件属性进行了注释,并且在文件的最后一个句柄关闭时更新其大小。

请参阅Old New Thing Blog 了解更多详情。

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

【讨论】:

  • 感谢您提供博客链接。它解释了很多事情。虽然在我看来这是_stat() 中的一个错误,但它使用的函数可能会获取我的陈旧数据。
  • 这不是唯一的怪癖。 Windows 有一个称为文件系统隧道的功能,它会在一定时间内报告相同的文件大小。这确实会浮出水面,例如登录到文件,关闭,重命名并再次打开同名文件。见blogs.msdn.com/b/oldnewthing/archive/2005/07/15/439261.aspx
  • 呃。这看起来像是取悦懒惰或糟糕的编码人员的不合理尝试。如果我希望文件具有相同的时间戳,我会自己处理。
【解决方案2】:

使用 GetFileInformationByHandle 获取确切的大小。

【讨论】:

    【解决方案3】:

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

    可能的解决方案是使用以下 Win32 API 选项之一:

    1. 如果您碰巧使用CreateFile 打开文件,请使用FILE_FLAG_NO_BUFFERINGFILE_FLAG_WRITE_THROUGH 标志。
    2. 完成写入后,调用FlushFileBuffers 以确保刷新元数据(您必须为此获取文件句柄)。

    这应该不会对性能造成太大影响,假设您的 C++ 流正在做自己的缓冲。

    【讨论】:

    • 系统不应该确保其内部数据结构之间的一致性吗?我实际上已经重做了代码,以便它在_stat() 调用之前执行stream.close(),所以现在它可以工作了。
    • @wilx,一致性是有代价的。在不确保一致性的情况下,缓存工作得更好,因此由您决定哪个对您的应用程序更重要。很高兴你解决了你的问题,看看我的建议是否也能解决它会很有趣。
    • 当然可以,但一致性和正确性比速度重要得多。
    • @wilx,如果这总是正确的,那么您将不会有 C++ 缓冲流。您确实拥有它们,因为它们在没有比赛的情况下更快,更安全。如果比赛是可能的,你必须使用同步手段。下面的级别也是如此:默认情况下您获得速度,“手动”意味着保持一致性。
    猜你喜欢
    • 2016-02-02
    • 2013-11-14
    • 2016-11-16
    • 1970-01-01
    • 1970-01-01
    • 2015-12-04
    • 2015-05-02
    • 2016-01-06
    • 1970-01-01
    相关资源
    最近更新 更多