.NET 2.0: File.AppendAllText(...) - Реализация потоковой безопасности

Как упражнение в незанятом любопытстве больше всего на свете, рассмотрите следующий простой класс ведения журнала:

internal static class Logging
{
    private static object threadlock;

    static Logging()
    {
        threadlock = new object(); 
    }

    internal static void WriteLog(string message)
    {
        try
        {
            lock (threadlock)
            {
                File.AppendAllText(@"C:\logfile.log", message);
            }
        }
        catch
        {
            ...handle logging errors...
        }
    }
}

Требуется ли lock вокруг File.AppendAllText(...) или является ли этот метод неотъемлемо потокобезопасным по своей собственной реализации?

Поиск информации об этом дает много противоречивой информации, некоторые говорят "да", некоторые говорят "нет". MSDN ничего не говорит.

Ответ 1

File.AppendAllText собирается получить эксклюзивную блокировку записи в файле журнала, что приведет к тому, что любой параллельный поток попытается получить доступ к файлу, чтобы выбросить исключение. Так что да, вам нужен объект статической блокировки, чтобы предотвратить одновременное использование нескольких потоков от записи в файл журнала и повышение IOException.

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

В качестве альтернативы вы можете использовать TextWriterTraceListener, который является потокобезопасным (ну, он будет делать блокировку для вас; скорее напишите как можно меньше моего собственного многопоточного кода).

Ответ 2

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

Ответ 3

Тестирование параллельных записей показывает, что вы получите сообщение System.IO.IOException, если вы хотите прокомментировать свою инструкцию блокировки.

[Test]
public void Answer_Question()
{
    var ex = Assert.Throws<AggregateException>(() => Parallel.Invoke(
        () => Logging.WriteLog("abc"),
        () => Logging.WriteLog("123")
    ));

    // System.IO.IOException: The process cannot access the file 'C:\Logs\thread-safety-test.txt' because it is being used by another process.
    Console.Write(ex);
}