Итак, у меня есть статический класс, который предполагается использовать в качестве менеджера файлов журналов, который может добавлять "сообщения" (строки) к объекту Queue и выводит сообщения в файл. Проблема заключается в том, что многие разные потоки должны быть в очереди, и что писатель должен быть асинхронным. В настоящее время, когда я вставляю в очередь, я также проверяю, записывает ли писатель (проверка bool), если это не так, я устанавливаю bool и начинаю писать, но я получаю прерывистые исключения IO в отношении доступа к файлам, а затем более странное поведение.
Кто-то хочет дать мне руку на это?
Похоже, что очередь запускает операцию записи файла. Я рекомендую вам инвертировать отношение управления, чтобы записывающее устройство управляло процессом и вместо этого проверял очередь на работу.
Самый простой способ реализовать это - добавить механизм опроса для писателя, в котором он проверяет очередь на работу с регулярными интервалами.
В качестве альтернативы вы можете создать класс наблюдаемой очереди, который уведомляет подписчиков (писателя) всякий раз, когда очередь переходит из пустого: позднее подписчик может начать свою работу. (В это время автор должен также отказаться от подписки в очереди или иным образом изменить способ реагирования на оповещения о очереди.)
После выполнения своей работы писатель затем проверяет очередь на работу. Если больше не нужно делать работу, она переходит в режим сна и возобновляет опрос или переходит в режим сна и переадресовывается к оповещениям о очереди.
Как Ирвин отметил в своем ответе, вам также необходимо использовать потокобезопасную оболочку, предоставляемую методом Queue
class 'Synchronized
или вручную синхронизировать доступ на ваш Queue
, если из него читаются несколько потоков и записываются в него (как в примере SpaceghostAli).
Вы должны синхронизировать свою очередь. Попросите несколько потоков отправить в очередь и один поток, прочитанный из очереди, и напишите в файл.
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);
...
}
У меня будет только один поток, делающий записи, чтобы избежать утверждений, в то время как я буду использовать несколько потоков для размещения в очереди.
Вам рекомендуется "Чтобы гарантировать безопасность потока в очереди, все операции должны выполняться через оболочку, возвращаемую методом Synchronized". - от http://msdn.microsoft.com/en-us/library/system.collections.queue.aspx
Позвольте мне решить проблему на другом уровне:
Если вы пишете бизнес-приложение, то вы хотите сосредоточиться на части бизнес-логики, а не на инфраструктурном коде, тем более, если этот инфра-код уже доступен, протестирован и развернут на нескольких производственных площадках (заботясь о своем НСФО)
Я уверен, что вы знаете о существовании фреймворков регистрации, таких как log4net и других http://csharp-source.net/open-source/logging.
Вы дали им шанс перед тем, как вручную выпустить свой собственный Logger?
Возьмите этот вариант техническому архитектору предприятия, на которого вы пишете, и увидите, что она думает.
Приветствия
Если вы не хотите резко перестраивать свой код, как я предложил в другом ответе, вы можете попробовать это, которое предполагает, что ваш класс 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);
}
}
}