Многопоточная постановка в очередь записи в файл

2

Итак, у меня есть статический класс, который предполагается использовать в качестве менеджера файлов журналов, который может добавлять "сообщения" (строки) к объекту Queue и выводит сообщения в файл. Проблема заключается в том, что многие разные потоки должны быть в очереди, и что писатель должен быть асинхронным. В настоящее время, когда я вставляю в очередь, я также проверяю, записывает ли писатель (проверка bool), если это не так, я устанавливаю bool и начинаю писать, но я получаю прерывистые исключения IO в отношении доступа к файлам, а затем более странное поведение.

Кто-то хочет дать мне руку на это?

Теги:
multithreading
file-io

5 ответов

2
Лучший ответ

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

Самый простой способ реализовать это - добавить механизм опроса для писателя, в котором он проверяет очередь на работу с регулярными интервалами.

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

После выполнения своей работы писатель затем проверяет очередь на работу. Если больше не нужно делать работу, она переходит в режим сна и возобновляет опрос или переходит в режим сна и переадресовывается к оповещениям о очереди.

Как Ирвин отметил в своем ответе, вам также необходимо использовать потокобезопасную оболочку, предоставляемую методом Queue class 'Synchronized или вручную синхронизировать доступ на ваш Queue, если из него читаются несколько потоков и записываются в него (как в примере SpaceghostAli).

2

Вы должны синхронизировать свою очередь. Попросите несколько потоков отправить в очередь и один поток, прочитанный из очереди, и напишите в файл.

public void Log(string entry)
{
    _logMutex.WaitOne();
    _logQueue.Enqueue(entry);
    _logMutex.ReleaseMutex();
}

private void Write()
{
    ...
    _logMutex.WaitOne();
    string entry = _logQueue.Dequeue();
    _logMutex.ReleaseMutex();

    _filestream.WriteLine(entry);
    ...
}
2

У меня будет только один поток, делающий записи, чтобы избежать утверждений, в то время как я буду использовать несколько потоков для размещения в очереди.

Вам рекомендуется "Чтобы гарантировать безопасность потока в очереди, все операции должны выполняться через оболочку, возвращаемую методом Synchronized". - от http://msdn.microsoft.com/en-us/library/system.collections.queue.aspx

  • 0
    Хотите разработать синхронизированный метод?
  • 0
    Более подробно с примерами здесь: msdn.microsoft.com/en-us/library/…
Показать ещё 1 комментарий
1

Позвольте мне решить проблему на другом уровне:

Если вы пишете бизнес-приложение, то вы хотите сосредоточиться на части бизнес-логики, а не на инфраструктурном коде, тем более, если этот инфра-код уже доступен, протестирован и развернут на нескольких производственных площадках (заботясь о своем НСФО)

Я уверен, что вы знаете о существовании фреймворков регистрации, таких как log4net и других http://csharp-source.net/open-source/logging.

Вы дали им шанс перед тем, как вручную выпустить свой собственный Logger?

Возьмите этот вариант техническому архитектору предприятия, на которого вы пишете, и увидите, что она думает.

Приветствия

1

Если вы не хотите резко перестраивать свой код, как я предложил в другом ответе, вы можете попробовать это, которое предполагает, что ваш класс LogManager имеет:

  • статическая потокобезопасная очередь, _SynchronizedQueue
  • статический объект для блокировки при записи, _WriteLock

и эти методы:

public static void Log(string message) {
    LogManager._SynchronizedQueue.Enqueue(message);
    ThreadPool.QueueUserWorkItem(LogManager.Write(null));
}

// QueueUserWorkItem accepts a WaitCallback that requires an object parameter
private static void Write(object data) {
    // This ensures only one thread can write at a time, but it dangerous
    lock(LogManager._WriteLock) {
        string message = (string)LogManager._SynchronizedQueue.Dequeue();
        if (message != null) {
            // Your file writing logic here
        }
    }
}

Есть только одна проблема: оператор блокировки в методе Write выше гарантирует, что только один поток может писать за раз, но это опасно. Многие могут ошибаться при попытке записи в файл, и вы не хотите удерживать (блокировать) потоки пулов потоков на неопределенный срок. Поэтому вам нужно использовать объект синхронизации, который позволяет указать тайм-аут, например Monitor, и переписать метод Write следующим образом:

private static void Write() {
    if (!Monitor.TryEnter(LogManager._WriteLock, 2000)) {
       // Do whatever you want when you can't get a lock in time
    } else {
      try {
         string message = (string)LogManager._SynchronizedQueue.Dequeue();
         if (message != null) {
             // Your file writing logic here
         }
      }
      finally {
        Monitor.Exit(LogManager._WriteLock);
      }
    }
}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню