For my console application, I have implemented a simple file logger. The logger uses StringBuilder
to keep appending log entries and writes the LogText
data to the LogFile
at the end. This results in having only one file I/O operation. The application execution must be extremely fast and hence, I have implemented Parallel.ForEach
along with async
-await
and reduced I/O operations wherever possible.
The problem is that Logger
is not thread safe. Using lock
or Monitor
to synchronize the shared resource logger
inside Parallel.ForEach
loop slows down the performance. Is there any optimal way to synchronize the shared resource which does not affect execution speed much?
I am open to alternative approaches or suggestions.
Logger.cs
public class Logger
{
private readonly string LogFile;
private readonly StringBuilder LogText;
public Logger(string logFile)
{
LogFile = logFile;
LogText = new StringBuilder();
}
public void Write(string message)
{
LogText.AppendLine(message);
}
public void WriteToFile()
{
File.AppendAllText(LogFile, LogText.ToString());
}
}
Program.cs
public class Program
{
public static void Main(string[] args)
{
string logFile = args[0];
string workingDirectory = args[1];
Logger logger = new Logger(logFile);
logger.Write($"INFO | Execution Started");
try
{
List<string> files = Directory.EnumerateFiles(workingDirectory, "*", SearchOption.AllDirectories).ToList();
Parallel.ForEach(files, async file =>
{
List<string> results = await PerformCPUBoundComputationAsync();
foreach(string result in results)
{
logger.Write($"INFO | Item: {result}");
}
string response = await MakePostRequestAsync(results);
logger.Write($"INFO | Response: {response}");
});
}
catch (Exception ex)
{
logger.Write($"ERROR | {ex.Message}");
}
finally
{
logger.Write($"INFO | Execution Ended");
logger.WriteToFile();
}
}
}