public void Test_Multiple_Threads() { var mlogger = new MultiThreadedCommonLogger(); var tasks = new List <Task>(); for (var i = 0; i < 100; ++i) { var copy = i; tasks.Add(Task.Run(() => mlogger.Error(copy.ToString()))); tasks.Add(Task.Run(() => mlogger.Info(copy.ToString()))); } Task.WaitAll(tasks.ToArray()); var sbOut = new StringBuilder(); var sbErr = new StringBuilder(); mlogger.DumpAllLogs(s => sbOut.AppendLine(s), s => sbErr.AppendLine(s)); Assert.That(sbOut.ToString(), Contains.Substring("99"), "On completion 99 must be part of the output stream"); Assert.That(sbErr.ToString(), Contains.Substring("99"), "On completion 99 must be part of the error stream"); Assert.That(mlogger.HadErrors, Is.True); }
private static void Execute(Options options) { var directLog = new ConsoleLogger(); var threadedLog = new MultiThreadedCommonLogger(); try { var io = new IO(); directLog.Info("Comparing directories"); directLog.Info("\t[A] {0}", options.DirectoryA); directLog.Info("\t[B] {0}", options.DirectoryB); var foundComparers = _getComparers(io); _dumpComparers(foundComparers, directLog); var foundFiles = new FileGatherer(io, options.DirectoryA, options.DirectoryB).CreateFilelist(); var correlator = new FileToComparerCorrelator(directLog); // Note: not thread safe log, assuming correlator is single threaded var matchedFiles = correlator.MatchFilesToComparers(foundComparers, foundFiles); directLog.Info("Processing files"); _processFiles(threadedLog, matchedFiles); // Finally always dump remaining logs threadedLog.DumpAllLogs(_writeToOutput, _writeToError); directLog.Info("Processing files finished"); } catch (Exception ex) when(!System.Diagnostics.Debugger.IsAttached) { Console.Error.WriteLine(ex); Environment.ExitCode = 2; } // compare errors should not indicate serious (exit code 2) if (threadedLog.HadErrors) { directLog.Warning("There were errors. Examine the output for details!"); Environment.ExitCode = 1; } else { directLog.Info("Completed without errors."); } }
/// <summary> /// Force a locked flush of all threads to report some progress to the console IO. /// Otherwise it will be blank until the entire task queue has been completed /// </summary> /// <param name="logger"></param> private static void _syncLogs(MultiThreadedCommonLogger logger) { while (_finishEvent.IsSet == false) { try { logger.DumpAllLogs(_writeToOutput, _writeToError); } catch (Exception e) { logger.Exception("Syncing dump failed failed with", e); } // Since it is supposed to be a separate long running thread, freeze it Thread.Sleep(2000); } }
/// <summary> /// Processes files in an async manner, avoids locking of IO or any other operations when executed in parallel /// </summary> /// <param name="logger"></param> /// <param name="matchedFiles">An lazy evaluated list that performs IO to disk on demand</param> private static void _processFiles(MultiThreadedCommonLogger logger, IEnumerable <Tuple <CompareEntry, List <IVFComparer> > > matchedFiles) { var tasks = new List <Task>(); _finishEvent = new CountdownEvent(1); foreach (var matchedFile in matchedFiles) { var mf = matchedFile; tasks.Add(Task.Run(() => _executeTask(logger, mf.Item1, mf.Item2)).ContinueWith((task) => _checkForErrors(logger, task))); } // Finally spawn a standalone long running task var syncTask = Task.Factory.StartNew(() => _syncLogs(logger), TaskCreationOptions.LongRunning); Task.WaitAll(tasks.ToArray()); // All processing tasks are completed, now signal the sync task to complete _finishEvent.Signal(); syncTask.Wait(); }