简体   繁体   中英

Why nothing is printed on the console?

I create 2 methods that print x and y 100 times. I want them to run concurrent and I expect the output to be xxxxyxyxyyyxyyxyx... sthg like that. It doesn't print anything. Am I missing some logic here?

using System;
using System.Threading.Tasks;

namespace ConsoleApplication32
{
    internal class Program
    {
        public static async Task<int> Print1()
        {
            await Task.Run(() =>
            {
                for (int i = 0; i < 100; i++)
                {
                    Console.Write("x");
                }
            });

            return 1;
        }

        public static async Task<int> Print2()
        {
            await Task.Run(() =>
            {
                for (int i = 0; i < 100; i++)
                {
                    Console.Write("y");
                }
            });

            return 1;
        }

        public static void Run()
        {
            Task<int> i = Print1();
            Task<int> k = Print2();
        }

        private static void Main(string[] args)
        {
            Run();
        }
    }
}

Explanation

There are a couple things we need to tweak to get the desired output: xxxxyxyxyyyxyyxyx .

  1. The Run method is not waiting for Task i and Task k to finish

    • This means that the console application will close before the two Task s complete
    • As Andrei Matracaru mentions in his answer, if you add Console.ReadLine() , it will prevent the console application from closing before the strings have been printed to the screen.
    • A more elegant solution is to use Task.WhenAll . Task.WhenAll will allow the code yield to its calling thread (in this case, the Main Thread) which doesn't freeze the UI of the application, and allows the application to continue to print to the screen until the Task s have completed. This may seem trivial for a simple console app, but it is crucial to never lock up the Main Thread when building any user-interactive application like mobile or web applications; locking up the Main Thread is what causing apps to "freeze".
    • Camilo Terevinto 's answer above recommends Task.WaitAll . This is also bad practice because it will also freeze the application; Task.WaitAll is not awaitable and cannot not yield to the calling thread.
  2. Print1() and Print2() never yield to the Main Thread

    • These methods are executing on a background thread, but they don't include an await inside of the for loop
    • An await is necessary inside of the for loop to allow CPU to continue executing the Main Thread, because Console.Write() can only print to the screen on the Main Thread.

Code

using System;
using System.Threading.Tasks;

namespace ConsoleApplication32
{
    internal class Program
    {
        public static async Task<int> Print1()
        {
            for (int i = 0; i < 100; i++)
            {
                Console.Write("x");
                await Task.Delay(10);
            }

            return 1;
        }

        public static async Task<int> Print2()
        {
            for (int i = 0; i < 100; i++)
            {
                Console.Write("y");
                await Task.Delay(10);
            }

            return 1;
        }

        public static async Task Run()
        {
            var i = Print1();
            var k = Print2();

            await Task.WhenAll(i, k);
        }

        private static void Main(string[] args)
        {
            Task.Run(async () => await Run()).GetAwaiter().GetResult();
        }
    }
}

Output

xyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxxyxyxyxy Press any key to continue...

C# 7.1 Alternative

C# 7.1 introduces the ability to have an async Main method. This means that we can tweak the Main method as follows:

    ...

    private static async Task Main(string[] args)
    {
        await Run();
    }
    ...

Tried your code and works just fine. Probably you think it doesn't work because the console window closes very fast. Add a Console.ReadLine() in your Main:

private static void Main(string[] args)
{
    Run();
    Console.ReadLine();
}

The problem is that you are not waiting for the tasks to complete, hence the program is terminating (no more synchronous code blocking).

If you want the tasks to run concurrently, do this instead:

public static void Run()
{
    Task<int> i = Print1();
    Task<int> k = Print2();
    Task.WaitAll(i, k);
}

The other way to wait for this tasks to complete, would be to do this:

public static async Task Run()
{
    Task<int> i = Print1();
    Task<int> k = Print2();
    await Task.WhenAll(i, k);
}

public static void Main(string[] args)
{
    Run().GetAwaiter().GetResult();
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM