【问题标题】:QFile: multiple handles to same physical file do not write all dataQFile:同一物理文件的多个句柄不会写入所有数据
【发布时间】:2016-02-15 14:24:15
【问题描述】:

我想知道QFile 在打开同一个文件的多个句柄时的行为(在 Windows 7 上使用 Visual Studio 2013 中的 C++),所以我编写了以下小程序:

  QFile file("tmp.txt");
  file.open(QIODevice::WriteOnly | QIODevice::Truncate);
  QTextStream ts(&file);
  ts << "Hallo\n";

  QFile file2("tmp.txt");
  file2.open(QIODevice::WriteOnly | QIODevice::Append);
  QTextStream ts2(&file2);
  ts2 << "Hallo 2\n";

  file.close();

  ts2 << "Hello again\n";

  file2.close();.

这会在文件 tmp.txt 中产生以下输出:

Hallo 2
Hello again

所以第一个Hallo 语句丢失了。如果我在ts &lt;&lt; "Hallo\n" 之后立即执行ts.flush(),则不会发生这种情况,这使我认为该语句在QString 的内部缓冲区中丢失了,或者它被后续的输出语句覆盖了。但是,我想在日志框架中使用QFile,所以我不想总是刷新,因为这会降低性能。

我也用std::basic_ostream&lt;char&gt;而不是QFile尝试了同样的事情:

  std::basic_ofstream<char> file;
  file.open("tmp.txt", std::ios_base::out | std::ios_base::ate | std::ios_base::app);
  file << "Hallo\n";

  std::basic_ofstream<char> file2;
  file2.open("tmp.txt", std::ios_base::out | std::ios_base::ate | std::ios_base::app);
  file2 << "Hallo 2\n";

  file.close();

  file2 << "Hello again\n";

  file2.close();

按照我的预期输出:

Hallo
Hallo 2
Hello again

那么QFile 的例子有什么问题呢? QFile 是否不打算与指向同一个文件的多个句柄一起使用,或者这里到底发生了什么?我认为我的用例很常见,所以发现这种行为让我有点惊讶。我在 Qt documentation 中找不到更多细节。我读过here Qt 以共享模式打开文件,所以这应该不是问题。

我最终想使用QFile 进行日志记录(其中对执行实际写入的函数的访问当然是同步的),但是这个小例子让我担心一些日志语句可能会在途中丢失。你认为使用 STL 流而不是 QFile 会更好吗?

编辑 正如所指出的,std::endl 会导致刷新,所以我将上面的 STL 示例更改为仅使用 \n,根据here 不会导致刷新。不过,上述行为没有改变。

【问题讨论】:

  • std::endl;也用过冲洗
  • 刚刚在没有std::endl 的情况下对其进行了测试 - 相同的行为(已编辑)

标签: c++ qt file qfile


【解决方案1】:

我似乎你想要它两种方式。

如果您想要多个写入缓冲区并且不想刷新它们,则很难确定文件中的所有写入都以正确的顺序进行。 您对std::basic_ostream 的小测试不能证明:它适用于更大的写入吗?它可以在其他操作系统上运行吗?您是否想为(尚未得到证实的)速度提升冒险您的流程?

【讨论】:

    【解决方案2】:

    有几件可疑的事情正在发生。首先,您引入了两个级别的缓冲。

    1. 首先,QTextStream 有一个内部缓冲区,您可以通过在其上调用 flush 来刷新。

    2. 其次,QFile 也正在缓冲(或者,更好的是,它使用库中的缓冲 API -- fopenfwrite 等)。将 QIODevice::Unbuffered 传递给 open 以使其使用无缓冲 API(openwrite、...)。

    现在因为这非常容易出错,QTextStream::flush actually flushes also the underlying file device

    另外,你传递WriteOnly | Append 这没有意义。这只是两者之一。


    但是,请注意,您的写入仍可能交错。 POSIX.1-2013 says that

    如果在一个操作中写入的全部量没有与来自任何其他进程的数据交错,则写入是原子的。当有多个写入器向单个读取器发送数据时,这很有用。应用程序需要知道可以以原子方式执行的写入请求有多大。此最大值称为 {PIPE_BUF}。

    (在 Windows 上我不知道)。

    【讨论】:

    • 关于两级缓冲:你说的有道理,但使用QTextStreams实际上也是Qt文档中的做法......doc.qt.io/qt-5/qfile.html。 FWIW,如果我不使用流,但直接使用file.write(),我会遇到相同的行为。关于标志:你是对的,我将其更改为仅使用 QIODevice::Append 并添加了 QIODevice::Unbuffered,但它并没有改变结果。
    • 因此,如果我使用QIODevice::Unbuffered 并使用file.write() 直接对文件进行操作,那么它会按照您的建议工作。但是,正如我在原始帖子中指出的那样,我想找出不使用刷新(或者在本例中使用缓冲区)以提高性能的方法。那么 STL 是否更胜一筹?
    【解决方案3】:
     QFile file("tmp.txt");
      file.open(QIODevice::WriteOnly | QIODevice::Truncate);
      QTextStream ts(&file);
      ts << "Hallo\n";
    
      QFile file2("tmp.txt");
      file2.open(QIODevice::Append);
      QTextStream ts2(&file2);
      ts2 << "Hallo 2\n";
    
      file.close();
    
      ts2 << "Hello again\n";
    
      file2.close();
    

    试试这个,我改变了它,所以截断不会被 WriteOnly 调用。愚蠢的我没有读过那个。 {更新}

    QIODevice::WriteOnly    0x0002  The device is open for writing. Note that this mode implies Truncate.
    QIODevice::Truncate 0x0008  If possible, the device is truncated before it is opened. All earlier contents of the device are lost.
    

    阅读来源:http://doc.qt.io/qt-5/qiodevice.html#OpenModeFlag-enum

    【讨论】:

    • 相同的行为 - 为什么二进制参数的顺序或相关?
    • 抱歉,请参阅我的更新。你能试试吗? ReadWrite 调用 Write 调用 Truncate。
    • 在这里找到它:doc.qt.io/qt-5/qiodevice.html#OpenModeFlag-enum 你去。
    • 好的,我将其更改为 file.open(QIODevice::WriteOnly)file2.open(QIODevice::Append) 但仍然是相同的行为
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-10
    • 2019-03-23
    • 2011-03-26
    • 2017-04-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多