简体   繁体   中英

Write reliably to same file from different processes

I did create a small C++ Tracing solution which works very well. Within one process all is well but when I open the output file from diferent processes the data gets not correctly written. I did open the file with FILE_SHARE_WRITE to be able to write to the file when it is already open. Then I did create a named mutex to ensure proper synchronisation between processes. But it appears that this is not enough. According to the MSDN this does work within one process but not between different processes. Next I tried to call FlushFileBuffers after every write while the mutex was still held but data was still distorted like this

The format is time process id/Thread id method enter/leave/severity namespace+method and then the message text.

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

I have looked at FILE_FLAG_NO_BUFFERING but it has severe limitations and it seems not easy to use .

Does anybody know the right way to write synchronized to the same file without distoriting the output?

Yours,

Alois Kraus

I finally got it working. The trick was to Seek at the end of the file before very write. Otherwise I would overwrite about half of my output although I do lock with a cross process mutex before every write.

The code looks like this

__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);
    }
}

SQLite uses file locks to ensure this doesn't happen to its database file when accessing it from multiple processes. Have you tried using LockFile ? ( example ). I have, in the past, used an SQLite database for logging from multiple processes, but then that's probably a bit too much in this case.

I don't know about the "right" way, but what you're doing already seems as right as it gets to me. The next possible solution that I can think of would be a dedicated process for writing to this file. The rest of the processes would communicate with the log-process through named pipes and (possibly) mutexes.

Perhaps you can even set it up so that there is no explicit process, but one of the running processes (the one which was started first) takes on this role. There would, of course, be further complications when this process ends and needs to pass the ownership of the file to another process. All in all it's not a very pretty solution, but it should work if all else fails.

Although I would suspect that there is still something neither of us has thought about, because there are programs which successfully use files for communication.

Hmm... on second thought - you already have timestamps available. Why not just make a browsing tool which sorts the records by timestamps? That way it would not matter what gets cached where.

Oh, and a third though - have you tried memory-mapped I/O? That's organized differently and it might be able to solve your problem (not to mention be more efficient).

As a starting point, I'd suggest ditching the shared write mode and making use of your named mutex to manage exclusive access to the file. If everything's working as it should, this should give you correct output. Then do some performance testing to see if you can afford to operate like this - you might find it's sufficient.

You have to protect or synchronize concurrent calls to the function that writes into the file with a named mutex. See CreateMutex

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM