【问题标题】:Reading a File while it is being written by another process在另一个进程写入文件时读取文件
【发布时间】:2013-03-01 09:17:28
【问题描述】:

我正在尝试从缓冲区文件中读取二进制数据,该文件由不同的进程(我无法修改)连续写入。我正在使用以下代码打开文件:

fileH = CreateFileA((LPCSTR)filename,
                    GENERIC_READ,
                    FILE_SHARE_READ | FILE_SHARE_WRITE,
                    NULL,
                    OPEN_EXISTING,
                    FILE_ATTRIBUTE_NORMAL, NULL);

它可以正确打开,没有错误。但是,当我从文件中读取数据时,由于我丢失了数据,它似乎阻止了其他进程写入文件。

缓冲区是循环的,这意味着文件大小是固定的,新数据会不断地覆盖缓冲区中的旧数据。

编辑: 有时最简单的解决方案会奏效......

我已联系软件公司并告诉他们该错误,并在一天之内发布了带有修复程序的新版本。 抱歉,这不适用于所有人。

【问题讨论】:

  • 同时读写文件是个坏主意。尝试使用MutexEvents来处理访问。
  • @bash.d,真的吗?告诉tail 和类似的实用程序。此外,您不能使用互斥锁和事件来同步进程。此外他说他不能修改其他应用程序。
  • 真正的问题是为什么您在 2013 年仍在使用非 Unicode API。您的意思是不是 CreateFileW
  • 你知道其他进程是否保持文件打开,或者它是否在每次需要写入时尝试打开文件?如果它试图以独占模式打开文件,当您打开文件以供读取时它将失败。与其他人所说的相反,通常让多个进程读取/写入同一个文件是完全可以的。
  • 过于本地化?认真的吗?

标签: c++ windows file winapi createfile


【解决方案1】:

如果不知道编写过程如何打开文件,很难说出您的选择。显然,它不是以独占方式打开文件并保持打开状态。否则你根本无法阅读。

您描述的行为表明写入过程以独占方式打开文件,写入文件,然后关闭文件。如果是这种情况,那么您不能让您的程序打开文件并保持打开状态。这将导致写入过程在尝试写入时失败。

如果你不能修改写作过程,那么你的选择是有限的,不是很有吸引力。最有可能的是,您必须让程序打开文件,读取一小块,关闭文件,然后在再次读取之前稍等片刻。即使这样,也不能保证在写入过程尝试写入时不会打开文件。我想,你已经发现了。

您知道写入过程是在无法打开文件时丢失数据,还是只是缓冲数据并在下次实际打开文件时写入?如果是这种情况,那么我建议一次逐步浏览文件的建议可能会奏效。否则,您将丢失数据。

据我所知,没有一种打开模式相当于“打开文件进行阅读,但如果有人想要独占访问,那就让他们拥有它。”

另一种可能性是让您的程序在您想阅读时重命名文件,然后在阅读后删除重命名的文件。当然,这假设写入过程将在必要时创建一个新文件。即便如此,如果在您重命名时写入过程尝试写入,可能会出现问题。我不认为这会是个问题(就文件系统而言,重命名可能是原子的),但这是你必须研究的东西。

【讨论】:

  • 数据在写入过程中确实丢失了,所以即使打开文件很短的时间,也会导致这种丢失。重命名文件听起来很有趣,我会检查一下。
【解决方案2】:

我建议查看优秀的Far Manager 的源代码。它的内部查看器可以轻松处理数千兆字节的文件,显示正在写入的文件没有问题,并且几乎可以实时更新更改的文件内容。我从未注意到正在显示的文件有任何阻塞问题。

问题相关的源码好像在viewer.cpp文件里。

一个有趣的事情是它使用GENERIC_READ

ViewFile.Open(strFileName, FILE_READ_DATA, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, nullptr, OPEN_EXISTING);

我怀疑在这里删除SYNCHRONIZE 可能很重要。

文件更改检测在Viewer::ProcessKey,KEY_IDLE情况下:

// Smart file change check -- thanks Dzirt2005
//
bool changed = (
    ViewFindData.ftLastWriteTime.dwLowDateTime!=NewViewFindData.ftLastWriteTime.dwLowDateTime ||
    ViewFindData.ftLastWriteTime.dwHighDateTime!=NewViewFindData.ftLastWriteTime.dwHighDateTime ||
    ViewFindData.nFileSize != NewViewFindData.nFileSize
);
if ( changed )
    ViewFindData = NewViewFindData;
else {
    if ( !ViewFile.GetSize(NewViewFindData.nFileSize) || FileSize == static_cast<__int64>(NewViewFindData.nFileSize) )
        return TRUE;
    changed = FileSize > static_cast<__int64>(NewViewFindData.nFileSize); // true if file shrank
}

缓存文件读取在cache.cpp 中实现。但是那里并没有什么真正惊天动地的东西,只有一些Seek()Read()eventually resultSetFilePointerExReadFile API 调用中)。不使用 OVERLAPPED。

【讨论】:

  • 如果它可以查看由另一个进程以独占模式打开的文件,我会感到非常惊讶。
  • 当然它不适用于独占打开的文件。但我发现它并不常见,尤其是对于日志文件。
猜你喜欢
  • 1970-01-01
  • 2012-10-22
  • 1970-01-01
  • 2021-01-23
  • 1970-01-01
  • 2015-03-16
  • 1970-01-01
  • 1970-01-01
  • 2011-03-13
相关资源
最近更新 更多