FRACerqueira / PromptPlus

Interactive command-line toolkit for .Net core with powerful controls and commands to create professional console applications.
https://fracerqueira.github.io/PromptPlus/
MIT License
50 stars 5 forks source link

PromptPlus output becomes messy from inside Parallel.ForEach or Parallel.Invoke functions #121

Open ividyon opened 8 months ago

ividyon commented 8 months ago

Tested with simple WriteLine.

From inside Parallel:

2024-01-13_12-01-33__WindowsTerminal

Without Parallel:

2024-01-13_12-07-45__WindowsTerminal

FRACerqueira commented 8 months ago

Hello ivideon, This behavior does not depend on the implementation of Prompt Plus but on the concept of parallelism functions that do not guarantee the execution sequence. Once prompplus knows what it will write to the console (stream out) it sends it to the default provider which "prints" the result. As the "print" does not have a guarantee of sequence due to parallelism, the result is confusing.

In other words, console operations do not work well with multi-threads unless the application itself manages the result and organizes the data before sending it to the console.

Try this and see the result (it will be confusing too)

        static void Main(string[] args)
        {
            Console.Clear();
            Console.WriteLine("write 100 line Without Parallel:");
            for (int i = 0; i < 100; i++) 
            {
                Console.Write("test Console.Write ");
                Console.WriteLine($"value is {i} ");

            }
            Console.WriteLine("Press Any Key");
            Console.ReadKey(false);
            Console.Clear();
            Console.WriteLine("write 100 line From inside Parallel.for:");
            Parallel.For(1,100,(i) =>
            {
                Console.Write("test Console.Write ");
                Console.WriteLine($"value is {i} ");
            });
        }
ividyon commented 8 months ago

Looks like this can be improved with the following:

internal static class Program
{
    internal static object ConsoleWriterLock = new object();

    static void Main(string[] args)
    {
        Console.Clear();
        Console.WriteLine("write 100 line Without Parallel:");
        for (int i = 0; i < 100; i++)
        {
            Console.Write("test Console.Write ");
            Console.WriteLine($"value is {i} ");
        }

        Console.WriteLine("Press Any Key");
        Console.ReadKey(false);
        Console.Clear();

        Console.WriteLine("write 100 line From inside Parallel.for:");
        Parallel.For(1, 100, (i) =>
        {
            lock (ConsoleWriterLock)
            {
                Console.Write("test Console.Write ");
                Console.WriteLine($"value is {i} ");
            }
        });
    }
}

Before: 2024-01-16_07-52-29__rider64

After: 2024-01-16_07-52-53__rider64

Using lock() {} with a static readonly object makes the output orderly during parallel processing, at (assumably) the cost of a bit of parallel performance (but we're probably talking microseconds here).

Some kind of PromptPlus.LockOutput boolean which enables such behavior, as well as access to a public PromptPlus.ConsoleLock object which is used for this (for making their own custom locks) could be great and save users the effort of wrapping each of their own Writes.

ividyon commented 8 months ago

Pressed the wrong button...

ividyon commented 7 months ago

I'll also note that this issue doesn't occur if you do .Write("MyString" + "\n") instead of WriteLine