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

QFile: multiple handles to same physical file do not write all data

我想知道当对同一个文件打开多个句柄时 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 << "Hallo\n" 之后立即执行 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 << "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. I've read here 中找到 Qt 以共享模式打开文件的更多细节,所以这应该不是问题。

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

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

 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

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

  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

A write is atomic if the whole amount written in one operation is not interleaved with data from any other process. This is useful when there are multiple writers sending data to a single reader. Applications need to know how large a write request can be expected to be performed atomically. This maximum is called {PIPE_BUF}.

(关于 Windows 我不知道)。

看来你想要两种方式。

如果您想要多个写入缓冲区并且不想刷新它们,则很难确定文件中的所有写入都以正确的顺序进行。 您使用 std::basic_ostream 进行的小型测试不是证明:它可以用于较大的写入吗?它可以在其他操作系统上运行吗?您想冒着风险来获得(尚未证实的)速度增益吗?