Надежная запись в один и тот же файл из разных процессов - PullRequest
8 голосов
/ 17 января 2011

Я создал небольшое решение C ++ Tracing, которое работает очень хорошо. Внутри одного процесса все хорошо, но когда я открываю выходной файл из разных процессов, данные записываются неправильно. Я открыл файл с помощью FILE_SHARE_WRITE, чтобы иметь возможность записи в файл, когда он уже открыт. Затем я создал именованный мьютекс, чтобы обеспечить правильную синхронизацию между процессами. Но, похоже, этого недостаточно. Согласно MSDN это работает внутри одного процесса, но не между разными процессами. Затем я попытался вызывать FlushFileBuffers после каждой записи, пока мьютекс еще удерживался, но данные все еще искажались, как это

Формат: идентификатор процесса времени / метод идентификатора потока, введите пространство имен / метода серьезности + метод и затем текст сообщения.

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 , но у него есть серьезные ограничения, и кажется, не прост в использовании .

Кто-нибудь знает правильный способ записи синхронизированных в тот же файл без искажения вывода?

Ваш,

Алоис Краус

Ответы [ 5 ]

7 голосов
/ 25 января 2011

Я наконец-то заработал.Хитрость заключалась в том, чтобы искать в конце файла, прежде чем самому писать.В противном случае я бы перезаписал примерно половину выходных данных, хотя блокирую с мьютексом перекрестного процесса перед каждой записью.

Код выглядит так

__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);
    }
}
2 голосов
/ 19 января 2011

SQLite использует блокировки файлов, чтобы этого не происходило с файлом базы данных при доступе к нему из нескольких процессов.Вы пытались использовать LockFile ?( Пример ).Раньше я использовал базу данных SQLite для ведения журнала от нескольких процессов, но тогда это, вероятно, слишком много в этом случае.

0 голосов
/ 19 января 2011

Вы должны защищать или синхронизировать одновременные вызовы функции, которая записывает в файл с именем мьютекса.См CreateMutex

0 голосов
/ 19 января 2011

В качестве отправной точки я бы предложил отключить режим общей записи и использовать ваш названный мьютекс для управления монопольным доступом к файлу. Если все работает как надо, это должно дать вам правильный вывод. Затем проведите тестирование производительности, чтобы выяснить, можете ли вы позволить себе работать таким образом - этого может оказаться достаточно.

0 голосов
/ 17 января 2011

Я не знаю о «правильном» пути, но то, что ты делаешь, уже кажется мне правильным. Следующее возможное решение, о котором я могу подумать, - это отдельный процесс записи в этот файл. Остальные процессы будут связываться с процессом регистрации через именованные каналы и (возможно) мьютексы.

Возможно, вы даже можете настроить его так, чтобы не было явного процесса, но один из запущенных процессов (тот, который был запущен первым) берет на себя эту роль. Конечно, могут возникнуть дальнейшие осложнения, когда этот процесс завершится, и необходимо передать право собственности на файл другому процессу. В общем, это не очень красивое решение, но оно должно работать, если ничего не помогает.

Хотя я подозреваю, что есть еще кое-что, о чем никто из нас не думал, потому что есть программы, которые успешно используют файлы для связи.

Хм ... если подумать - у вас уже есть метки времени. Почему бы просто не создать инструмент для просмотра, который сортирует записи по временным меткам? Таким образом, не имеет значения, что и где кэшируется.

Да, и третий - вы пробовали ввод-вывод с отображением в памяти? Это организовано по-другому и может решить вашу проблему (не говоря уже о том, чтобы быть более эффективной).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...