Ну,
Я подождал несколько дней, прежде чем решила опубликовать этот вопрос, поскольку я не был уверен, как это сказать, перейдя в длинный подробный пост. Тем не менее, я думаю, что на данный момент важно обратиться за помощью к сообществу.
В принципе, я пытался использовать NLog для настройки журналов для сотен потоков. Я думал, что это будет очень просто, но я получил это исключение через несколько десятков секунд: "InvalidOperationException: коллекция была изменена, операция перечисления не может выполняться"
Вот код.
//Launches threads that initiate loggers
class ThreadManager
{
//(...)
for (int i = 0; i<500; i++)
{
myWorker wk = new myWorker();
wk.RunWorkerAsync();
}
internal class myWorker : : BackgroundWorker
{
protected override void OnDoWork(DoWorkEventArgs e)
{
// "Logging" is Not static - Just to eliminate this possibility
// as an error culprit
Logging L = new Logging();
//myRandomID is a random 12 characters sequence
//iLog Method is detailed below
Logger log = L.iLog(myRandomID);
base.OnDoWork(e);
}
}
}
public class Logging
{
//ALL THis METHOD IS VERY BASIC NLOG SETTING - JUST FOR THE RECORD
public Logger iLog(string loggerID)
{
LoggingConfiguration config;
Logger logger;
FileTarget FileTarget;
LoggingRule Rule;
FileTarget = new FileTarget();
FileTarget.DeleteOldFileOnStartup = false;
FileTarget.FileName = "X:\\" + loggerID + ".log";
AsyncTargetWrapper asyncWrapper = new AsyncTargetWrapper();
asyncWrapper.QueueLimit = 5000;
asyncWrapper.OverflowAction = AsyncTargetWrapperOverflowAction.Discard;
asyncWrapper.WrappedTarget = FileTarget;
//config = new LoggingConfiguration(); //Tried to Fool NLog by this trick - bad idea as the LogManager need to keep track of all config content (which seems to cause my problem;
config = LogManager.Configuration;
config.AddTarget("File", asyncWrapper);
Rule = new LoggingRule(loggerID, LogLevel.Info, FileTarget);
lock (LogManager.Configuration.LoggingRules)
config.LoggingRules.Add(Rule);
LogManager.Configuration = config;
logger = LogManager.GetLogger(loggerID);
return logger;
}
}
Итак, я сделал свою работу, а не просто разместил свой вопрос здесь и получил семейное качество-время, я потратил на это выходные (Lucky Boy!) Я загрузил последнюю стабильную версию NLOG 2.0 и включил ее в свой проект. Я смог проследить точное место, где он взрывался:
в LogFactory.cs:
internal void GetTargetsByLevelForLogger(string name, IList<LoggingRule> rules, TargetWithFilterChain[] targetsByLevel, TargetWithFilterChain[] lastTargetsByLevel)
{
//lock (rules)//<--Adding this does not fix it
foreach (LoggingRule rule in rules)//<-- BLOWS HERE
{
}
}
в LoggingConfiguration.cs:
internal void FlushAllTargets(AsyncContinuation asyncContinuation)
{
var uniqueTargets = new List<Target>();
//lock (LoggingRules)//<--Adding this does not fix it
foreach (var rule in this.LoggingRules)//<-- BLOWS HERE
{
}
}
Проблема по мне
Итак, исходя из моего понимания, происходит то, что LogManager смешивается, поскольку есть вызовы config.LoggingRules.Add(Rule) из разных потоков, пока вызываются GetTargetsByLevelForLogger и FlushAllTargets.
Я попытался закрепить foreach и заменить его на цикл for, но логгер стал изгоем (пропуская много файлов журналов)
SOoooo НАКОНЕЦ
Во всем мире написано, что NLOG является потокобезопасным, но я пришел через несколько сообщений, которые все больше копают и утверждают, что это зависит от сценария использования. Как насчет моего дела?
Я должен создать тысячи регистраторов (не всех одновременно, но все же очень быстро).
Обходной путь, который я нашел, - создать все регистраторы в SAME MASTER THREAD; это ДЕЙСТВИТЕЛЬНО неудобно, поскольку я создаю все свои регистраторы приложений в начале приложения (вроде пула Logger). И хотя это работает сладко, это просто НЕ приемлемый дизайн.
Итак, вы знаете все это. Пожалуйста, помогите кодеру снова увидеть его семью.