【问题标题】:Concurrent File write between processes进程之间的并发文件写入
【发布时间】:2016-02-24 07:37:06
【问题描述】:

我需要将来自不同进程的日志数据写入单个文件。

我正在使用需要公共语言运行时支持的 Windows Mutex。

Mutex^ m = gcnew Mutex( false,"MyMutex" );
m->WaitOne();
//... File Open and Write ..
m->ReleaseMutex()

我真的需要从 C++ 更改为 C++/CLI 以进行同步吗?

如果不使用原子也可以。但我需要知道与本地互斥锁相比,使用此互斥锁是否会降低性能。

【问题讨论】:

  • 我很确定您在问什么:是否有没有 /clr 的不同解决方案(是的,很多),或者这种不同的解决方案是否明显更快(可能没有),或者什么否则...
  • 问题是你没有显示的#include,糟糕的sn-p。具有非托管线程原语(如 std::thread、std::mutex 等)定义的 .h 文件严格执行托管程序不能假定托管线程保证在非托管操作系统线程上运行的规则。这在实践中从未发生过,但在技术上是可行的。您不能#include 这些类型的文件,如有必要,请使用 pimpl 将它们排除在您自己的 .h 文件之外。
  • 使用 Windows Mutex 使工作变得简单。所以我想选择它。如果这不是一个好方法,那么我还能做些什么来从不同进程并发写入文件?
  • 使用 Boost::mutex 怎么样?

标签: c++ .net synchronization c++-cli mutex


【解决方案1】:

为您的 C++ 应用程序添加 CLR 支持只是为了获得 Mutex 类是多余的。您可以使用多种选项在两个应用程序之间同步文件访问。

选项 1:互斥体

如果您需要从多个进程写入文件,使用互斥锁是一种很好的方法。在 Win32 API 中使用 mutex functions。 (无论如何,.Net Mutex 类只是这些函数的包装。)

HANDLE mutex = CreateMutex(NULL, false, "MyMutex");

DWORD waitResult = WaitForSingleObject(mutex, INFINITE);
if (waitResult == WAIT_OBJECT_0)
{
    // TODO: Write the file
    WriteFile(...);

    ReleaseMutex(mutex);
}

正如另一个答案所述,您需要通过共享打开文件,以便您的两个应用程序可以同时打开它。但是,这本身可能还不够:如果您的两个应用程序都试图写入文件的同一区域,那么您仍然需要确保一次只有一个应用程序写入。想象一下,如果两个应用程序都查看文件的大小,然后都尝试同时写入该字节偏移量:即使两个应用程序都尝试只是追加到文件的末尾,它们最终还是互相破坏了。

选项 2:仅作为附加打开

如果您只是在文件末尾写入,并且从未尝试读取任何内容或写入文件末尾以外的任何位置,那么您可以使用一种特殊模式,让您不会使用互斥锁。如果您打开文件时将dwDesiredAccess 设置为FILE_APPEND_DATA | SYNCHRONIZE 没有别的(不包括FILE_WRITE_DATA),那么操作系统将确保所有写入的数据到最后的文件,写入数据的两个应用程序不会相互覆盖。这种行为记录在MSDN

如果仅设置了 FILE_APPEND_DATA 和 SYNCHRONIZE 标志,则调用者只能写入文件末尾,并且忽略有关写入文件的任何偏移信息。但是,对于此类写入操作,文件将根据需要自动扩展。

选项 3:锁定文件

您可以采取的另一种方法是使用LockFile 方法。使用LockFile(或LockFileEx),您可以让两个应用程序打开文件,并让每个应用程序锁定它要写入的文件部分。这为您提供了比互斥锁更多的粒度,允许同时发生非重叠写入。 (在整个文件上使用 LockFile 会给您带来与互斥锁相同的基本效果,另外还有一个好处是它可以防止其他应用程序在您这样做时写入文件。)有一个很好的例子来说明如何使用LockFile Raymond Chen's blog

【讨论】:

    【解决方案2】:

    实际上你根本不需要使用单独的互斥体,你可以使用文件本身。当使用 CreateFile API 调用(请参阅https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396)打开文件时,该调用采用一个名为 dwShareMode 的参数,该参数指定其他进程允许哪些并发访问。值 0 将阻止其他进程完全打开文件。

    几乎所有 API 都可以在后台打开文件映射到 CreateFile,因此当您打开文件进行写入时,clr 可能正在为您做正确的事情。

    在 C 运行时也有 _fsopen,它允许您打开带有共享标志的文件。

    我建议您在从 C# 打开文件时测试默认共享模式是什么。如果默认情况下它不阻止同时打开以进行写入,请使用 C 中的 _fsopen(或者可能有适当的 C# 函数)。

    【讨论】:

      猜你喜欢
      • 2023-03-03
      • 1970-01-01
      • 1970-01-01
      • 2012-09-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多