QFile:同一物理文件的多个句柄不会写入所有数据



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

  QFile file("tmp.txt");
  file.open(QIODevice::WriteOnly | QIODevice::Truncate);
  QTextStream ts(&file);
  ts << "Hallon";
  QFile file2("tmp.txt");
  file2.open(QIODevice::WriteOnly | QIODevice::Append);
  QTextStream ts2(&file2);
  ts2 << "Hallo 2n";
  file.close();
  ts2 << "Hello againn";
  file2.close();.

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

Hallo 2
Hello again

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

我也用std::basic_ostream<char>而不是QFile尝试了同样的事情:

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

输出如我所期望的那样:

Hallo
Hallo 2
Hello again

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

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

编辑正如有人指出的那样,std::endl会导致刷新,因此我将上面的 STL 示例更改为仅使用根据此处不会导致刷新的n。但是,上述行为保持不变。

我似乎你想要两种方式。

如果需要多个写入缓冲区并且不想刷新它们,则很难确定所有写入都按正确的顺序在文件中。你用std::basic_ostream的小测试不能证明:它适用于更大的写入吗?它会在其他操作系统上运行吗?您想冒险提高(尚未证实的(速度吗?

有几件可疑的事情正在发生。对于初学者,您将引入两个级别的缓冲。

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

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

现在,由于这非常容易出错,QTextStream::flush实际上也会刷新底层文件设备。

此外,您正在传递WriteOnly | Append这没有意义。这只是两者之一。


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

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

(在Windows上我不知道(。

 QFile file("tmp.txt");
  file.open(QIODevice::WriteOnly | QIODevice::Truncate);
  QTextStream ts(&file);
  ts << "Hallon";
  QFile file2("tmp.txt");
  file2.open(QIODevice::Append);
  QTextStream ts2(&file2);
  ts2 << "Hallo 2n";
  file.close();
  ts2 << "Hello againn";
  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

最新更新