I'm thinking it's impossible to have a pretty log when using Parallel.ForEach. Unless someone has a few tricks they can show me?
namespace Parallel_ForEach_Logging
{
class Program
{
private static uint _callCount = 0;
static void Main(string[] args)
{
IEnumerable<int> data = Enumerable.Range(0, 2);
Console.Out.WriteLine("***");
ParallelOptions options = new();
options.MaxDegreeOfParallelism = Environment.ProcessorCount;
Parallel.ForEach(data, options, datum =>
{
PrintNum(datum, _callCount );
});
Console.Out.Flush();
Console.Out.WriteLine("***");
}
public static void PrintNum(int num, uint callCount)
{
Console.Out.WriteLine($">>> IN {callCount}");
// Console.Out.WriteLine($" {num}");
Console.Out.WriteLine($"<<< OUT {callCount}");
PrintString($"\"{num.ToString()}\"", callCount);
}
public static void PrintString(string str, uint callCount)
{
Console.Out.WriteLine($">>> IN {callCount}");
// Console.Out.WriteLine($" {str}");
Console.Out.WriteLine($"<<< OUT {callCount}");
}
}
}
Here is what I think of when I say pretty log:
>>> IN: Method A
>>> IN : Method 1 called.
<<< OUT: Method 1 done.
>>> IN : Method 2 called.
<<< OUT: Method 2 done.
<<< OUT: Method A done in 32 milliseconds.
>>> IN: Method B
...
Here is a sample log that I'm currently getting:
>>> IN 1
>>> IN 0
<<< OUT 0
<<< OUT 1
>>> IN 0
<<< OUT 0
>>> IN 1
<<< OUT 1
Notice at the top there are two IN's back-to-back and then two OUT's back-to-back.
In the code _callCount is my attempt at figuring a call depth to use for indentation but I don't see how to make this work. Even if I did, I'm not sure I could do it without a million spaces in each log entry.
I know if I took the Parallel.ForEach out of the equation I could make this work for sure.
I'm okay if this truly is impossible to accomplish. I just need someone who is way smarter than me to say so. ;)
CodePudding user response:
You could lock your call to PrintString. Keep in mind that doing so will stall your other threads until the current thread releases the lock. So in this case by doing so your code would effectively be sequential, but the order of which threads print will be effectively random.
CodePudding user response:
The only other way i could think of except locking the thread would be to use a ConcurrentBag containing the call count and the method name in a Tuple or a class. Then after the ForEach is done you could sort this collection by using call count and method name so that's it's displayed in sequential fashion.
That's probably cheating since it wouldn't be real time but other than that i have no idea how you could achieve this.