Безопасен ли поток ColoredConsoleAppender?
У меня есть многопоточное консольное приложение, в котором я использую log4net.Appender.ColoredConsoleAppender
для вывода сообщений об ошибках, напечатанных в окне консоли.
Я также использую неблокирующее консольное устройство чтения, которое описано здесь: https://stackoverflow.com/a/18342182/1688642
Иногда приложение блокирует и нажимает клавишу возврата (Enter) в консоли, чтобы удалить тупик.Это всегда сопровождается ошибкой из log4net (через ColoredConsoleAppender).Я подозреваю, что существует тупик между Console.ReadLine в считывателе консоли и записью внутри ColoredConsoleAppender (который не является простым Console.WriteLine).
Я посмотрел на исходный код ColoredConsoleAppender и онбыло гораздо сложнее, чем я думал, и я подозреваю, что это не потокобезопасно.
Я также читал о потенциальной тупиковой ситуации, которая может возникнуть между Console.ReadLine и Console.WriteLine, описанной здесь: http://blogs.microsoft.co.il/dorony/2012/09/12/consolereadkey-net-45-changes-may-deadlock-your-system/ но я пришел к выводу, что это не та же проблема.
Обновление 1: Приведенный ниже код является иллюстрацией, а не реальным кодом.И этот код не блокируется ....
using System;
using System.IO;
using System.Reflection;
using System.Threading;
using log4net;
using log4net.Config;
...
class Program
{
static void Main(string[] args)
{
var exeLocation = new FileInfo(Assembly.GetEntryAssembly().Location);
var appConfig = new FileInfo(Path.Combine(exeLocation.DirectoryName, Assembly.GetEntryAssembly().GetName().Name + ".exe.config"));
XmlConfigurator.Configure(appConfig);
// Start two threads that writes log messages
Thread t1 = new Thread(ThreadLoop);
t1.Start("T1");
Thread t2 = new Thread(ThreadLoop);
t2.Start("T2");
ILog log = LogManager.GetLogger("MAIN_LOG");
Console.Write("$ ");
while (true)
{
string line;
if (Reader.TryReadLine(out line, 100))
{
bool handled = ParseAndExecuteCommand(line);
if (!handled)
{
Console.WriteLine("Unknown command (type 'h' to get help).");
}
Console.Write("$ ");
}
log.Info($"Info from main {Environment.TickCount}");
Console.WriteLine($"Console.WriteLine from main {Environment.TickCount}");
}
}
private static void ThreadLoop(object name)
{
while (true)
{
Thread.Sleep(1000);
ILog log = LogManager.GetLogger("THREAD_LOG");
log.Info($"Info from thread {name} {Environment.TickCount}");
log.Warn($"Warning from thread {name} {Environment.TickCount}");
log.Error($"Error from thread {name} {Environment.TickCount}");
Console.WriteLine($"Console.WriteLine from thread {name} {Environment.TickCount}");
}
}
Ниже приведена конфигурация log4net из файла App.config:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
</configSections>
<log4net>
<appender name="ConsoleAppender" type="log4net.Appender.ColoredConsoleAppender">
<mapping>
<level value="ERROR" />
<foreColor value="Red, HighIntensity" />
</mapping>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
</layout>
<threshold value="ERROR" />
</appender>
<root>
<level value="INFO" />
<appender-ref ref="ConsoleAppender" />
</root>
</log4net>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" />
</startup>
</configuration>