可靠地写入来自不同进程的同一文件

时间:2011-01-17 10:30:09

标签: c++ c windows winapi

我确实创建了一个很好的小型C ++跟踪解决方案。在一个进程中一切都很好但是当我从不同的进程打开输出文件时,数据没有被正确写入。我确实用FILE_SHARE_WRITE打开文件,以便在文件打开时写入文件。然后我创建了一个命名的互斥锁,以确保进程之间的正确同步。但似乎这还不够。根据MSDN,这确实在一个过程中起作用,但在不同过程之间起作用。 接下来我尝试在每次写入后调用FlushFileBuffers,而互斥锁仍被保持,但数据仍然像这样扭曲

格式是时间进程id /线程id方法输入/ leave / severity命名空间+方法然后是消息文本。

10:29:42.994 7448/2236       }} Dll2.Test.fndll2 L1 -> Duration: 0.094s
10:29:43.040 7448/2236 {{       Dll2.DllMain L1
10:29:43.134 7448/2236 Info     Dll2.DllMain L1 Process detach
10:29:43.181 7448/2236       }} Dll2.DllMain L1 -> Duration: 0.141s
     }} Dll2.DllMain L1 -10:29:42.681 7448/2236 Info     Dll1.DllMain L1 Process attach
10:29:42.728 7448/2236       }} Dll1.DllMain L1 -10:29:42.744 2216/5510:29:42.775 7448/2236 {{       Dll1.Test.fndll1 10:210:29:42.822 7448/2236 Info     Dll1.Test.fndll1 10:29:42.837 2216/557610:29:42.853 7448/2236       }} Dll1.Test.fndll1 L110:29:42.884 2216/557610:29:43.306 7448/2236 {{       Dll1.DllMain L1
10:29:43.353 7448/2236 Info     Dll1.DllMain L1 Process detach
10:29:43.400 7448/2236       }} Dll1.DllMain L1 -> Duration: 0.094s

我看过FILE_FLAG_NO_BUFFERING,但它有严重的限制,似乎not easy to use

是否有人知道在不降低输出的情况下将同步写入同一文件的正确方法?

你的,

Alois Kraus

5 个答案:

答案 0 :(得分:7)

我终于开始工作了。诀窍是在写入之前先找到文件的末尾。否则我会覆盖大约一半的输出,尽管我在每次写入之前都会使用跨进程互斥锁。

代码看起来像这样

__int64 Seek (HANDLE hf, __int64 distance, DWORD MoveMethod)  // from MSDN 
{
   LARGE_INTEGER li;
   li.QuadPart = distance;
   li.LowPart = SetFilePointer (hf, 
                                li.LowPart, 
                                &li.HighPart, 
                                MoveMethod);

   if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR)
   {
      li.QuadPart = -1;
   }

   return li.QuadPart;
}



void WriteToFile(TCHAR *pData)
{
    DWORD dwWritten = 0;

    if( FALSE == ::WriteFile(_hFile, pData, _tcslen(pData)*sizeof(TCHAR), &dwWritten, NULL) )
    {
        _LastError = ::GetLastError();
        ASSERT__(FALSE);
    }
}

virtual void Write(TCHAR *pStr)
{
    if( _hWriteMutex != NULL )
    {
        DWORD res = ::WaitForSingleObject(_hWriteMutex, 120*1000);
        if( res == WAIT_OBJECT_0 || res == WAIT_ABANDONED ) // another process might have crashed while holding the mutex
        {
            // Ensure that we are really writing at the end of the file 
            __int64 fPos = Seek(_hFile, 0, FILE_END);
            WriteToFile(pStr);
            ::ReleaseMutex(_hWriteMutex);
        }
        else
        {
            ASSERT__(FALSE);
        }
    }
    else
    {
        WriteToFile(pStr);
    }
}

答案 1 :(得分:2)

SQLite使用文件锁来确保在从多个进程访问数据库文件时不会发生这种情况。您是否尝试过使用LockFile? (example)。过去,我使用SQLite数据库从多个进程进行日志记录,但在这种情况下,这可能有点太多了。

答案 2 :(得分:0)

我不知道“正确”的方式,但你所做的事情似乎已经对我有用了。我能想到的下一个可能的解决方案是写入此文件的专用过程。其余的进程将通过命名管道和(可能)互斥体与日志进程通信。

也许您甚至可以将其设置为没有显式进程,但其中一个正在运行的进程(首先启动的进程)具有此角色。当然,当这个过程结束并且需要将文件的所有权传递给另一个进程时,会有进一步的复杂化。总而言之,这不是一个非常漂亮的解决方案,但如果一切都失败了,它应该可以工作。

虽然我怀疑仍然没有人想到过,因为有些程序可以成功地使用文件进行交流。

嗯......第二个想法 - 你已经有了时间戳。为什么不制作一个按时间戳对记录进行排序的浏览工具?这样,将缓存放在哪里并不重要。

哦,还有第三个 - 你尝试过内存映射的I / O吗?这是有组织的,它可能能够解决你的问题(更不用说更有效)。

答案 3 :(得分:0)

作为一个起点,我建议放弃共享写入模式并利用您指定的互斥锁来管理对该文件的独占访问。如果一切正常,这应该给你正确的输出。然后进行一些性能测试,看看你是否能负担得起这样的操作 - 你可能会发现它已经足够了。

答案 4 :(得分:0)

您必须保护或同步对使用命名互斥锁写入文件的函数的并发调用。见CreateMutex