C# - Почему я получаю LockRecursionException при использовании ReaderWriterLockSlim? - PullRequest
0 голосов
/ 04 мая 2020

Я работаю над многопоточной программой. Я пытаюсь создать простую систему регистрации с двумя типами журналов: «INFO» и «ERROR». Я создал класс под названием «Logger» и два метода stati c: Info и Error. Я также создал stati c ReadWriterLockSlim, чтобы только один поток одновременно мог записывать в файл журнала.

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

System.Threading.LockRecursionException: 'Recursive write lock acquisitions not allowed in this mode.'

Вот код класса Logger:

public class Logger
{
    const string filePath = "Log.txt";
    static private ReaderWriterLockSlim writeLock = new ReaderWriterLockSlim();
    public static string Info(string log)
    {
        StringBuilder sb = new StringBuilder();
        string logMessage = DateTime.Now.ToString() + " | INFO | " + log;
        writeLock.EnterWriteLock();
        using (StreamWriter writetext = File.AppendText(filePath))
        {
            writetext.WriteLine(logMessage);
            Console.WriteLine(logMessage);
        }
        writeLock.ExitWriteLock();
        return logMessage;
    }

    public static string Error(string log)
    {
        StringBuilder sb = new StringBuilder();
        string logMessage = DateTime.Now.ToString() + " | ERROR | " + log;
        writeLock.EnterWriteLock();
        using (StreamWriter writetext = File.AppendText(filePath))
        {
            writetext.WriteLine(logMessage);
            Console.WriteLine(logMessage);
        }
        return logMessage;
    }

}

Надеюсь, кто-то может помочь Я понимаю, что я сделал не так и как это исправить. Спасибо всем заранее.

1 Ответ

1 голос
/ 04 мая 2020

Ваша проблема в вашем Error методе: он берет блокировку, но не снимает ее снова.

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

public static string Error(string log)
{
    StringBuilder sb = new StringBuilder();
    string logMessage = DateTime.Now.ToString() + " | ERROR | " + log;
    writeLock.EnterWriteLock();
    try
    {
        using (StreamWriter writetext = File.AppendText(filePath))
        {
            writetext.WriteLine(logMessage);
            Console.WriteLine(logMessage);
        }
    }
    finally
    {
        writeLock.ExitWriteLock();
    }
    return logMessage;
}

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

public class Logger
{
    const string filePath = "Log.txt";
    private static readonly object lockObject = new object();
    public static string Info(string log)
    {
        StringBuilder sb = new StringBuilder();
        string logMessage = DateTime.Now.ToString() + " | INFO | " + log;
        lock (lockObject)
        {
            using (StreamWriter writetext = File.AppendText(filePath))
            {
                writetext.WriteLine(logMessage);
                Console.WriteLine(logMessage);
            }
        }
        return logMessage;
    }

    public static string Error(string log)
    {
        StringBuilder sb = new StringBuilder();
        string logMessage = DateTime.Now.ToString() + " | ERROR | " + log;
        lock (lockObject)
        {
            using (StreamWriter writetext = File.AppendText(filePath))
            {
                writetext.WriteLine(logMessage);
                Console.WriteLine(logMessage);
            }
        }
        return logMessage;
    }
}
...