1

Can anyone explain the difference of using Task.Run with or without async and await like the code below, e.g. await Task.Run(async ()=>...)?

 public class AsyncRun
    {

        public void Entry()
        {
            Test4().Wait();
        }

        private async Task Test4()
        {
            Console.WriteLine($"1 {DateTime.Now}");

            await Task.Run(async () => await Get());

            Console.WriteLine($"2 {DateTime.Now}");

            Console.Read();

            //1 23 / 05 / 2020 07:52:42
            //Get 1 23 / 05 / 2020 07:52:42
            //Get 2 23 / 05 / 2020 07:52:43
            //2 23 / 05 / 2020 07:52:43

        }


        private Task Test3()
        {
            Console.WriteLine($"1 {DateTime.Now}"); 

            Task.Run(async ()=> await Get());

            Console.WriteLine($"2 {DateTime.Now}");

            Console.Read();

            return Task.CompletedTask;

            //1 23 / 05 / 2020 07:47:24
            //2 23 / 05 / 2020 07:47:24
            //Get 1 23 / 05 / 2020 07:47:24
            //Get 2 23 / 05 / 2020 07:47:25
        }

        private async Task Test2()
        {
            Console.WriteLine($"1 {DateTime.Now}");
            await Task.Run(Get);

            Console.WriteLine($"2 {DateTime.Now}");

            Console.Read();

            //1 23 / 05 / 2020 07:43:24
            //Get 1 23 / 05 / 2020 07:43:24
            //Get 2 23 / 05 / 2020 07:43:25
            //2 23 / 05 / 2020 07:43:25
        }

        private void Test1()
        {
            Console.WriteLine($"1 {DateTime.Now}");
            Task.Run(Get);

            Console.WriteLine($"2 {DateTime.Now}");

            Console.Read();

            //1 23 / 05 / 2020 07:41:09
            //2 23 / 05 / 2020 07:41:09
            //Get 1 23 / 05 / 2020 07:41:09
            //Get 2 23 / 05 / 2020 07:41:10
        }

        private Task Get()
        {
            Console.WriteLine($"Get 1 {DateTime.Now}");
            Thread.Sleep(1000);
            Console.WriteLine($"Get 2 {DateTime.Now}");
            return Task.CompletedTask;
        }
    }
Pingpong
  • 6,402
  • 16
  • 57
  • 152
  • Does this answer your question? [Aync/Await action within Task.Run()](https://stackoverflow.com/questions/34755819/aync-await-action-within-task-run) – Pavel Anikhouski May 24 '20 at 15:43

2 Answers2

2

The difference is showing in your Console.WriteLines output.

When you use await Task.Run, you're waiting for the task you ran to finish, then continue the code execution, so you're getting the following logs:

// 1 23 / 05 / 2020 07:52:42
// Get 1 23 / 05 / 2020 07:52:42
// Get 2 23 / 05 / 2020 07:52:43
// 2 23 / 05 / 2020 07:52:43

When you don't wait the task you just ran, it's like you're running it and "forgetting" about it. This means that your code will continue to execute, and the task you ran will execute elsewhere, thus the logs are:

// 1 23 / 05 / 2020 07:47:24
// 2 23 / 05 / 2020 07:47:24
// ^ Your test code finished executing, without waiting for the Get task
// Get 1 23 / 05 / 2020 07:47:24
// Get 2 23 / 05 / 2020 07:47:25

Also, there is no difference between await Task.Run(async () => await Get()) and await Task.Run(Get) in terms of what actually happens. The only difference is that in the first one, you're creating another async lambda function to generate a Task while your Get method is already a Task, so you can use it directly.

Haytam
  • 4,250
  • 2
  • 10
  • 37
1

General notes

The Console is not a good environment to learn ot test multitasking in any way, shape or form. One big issue is with MT keeping the application alive, without blocking continuation code. In consoles we have to do that manually. GUI's are the right environment, as they do it by accident. The EventQueue keeps the application alive, while still allowing I/O to happen.

Thread.Sleep() should not be used in Get. You should use the more agnostic Task.Delay(1000) instead. Using Thread classes here is going to cause unwanted side effects and even ruin the reason we have await in the first place.

Try it again in a GUI and without Sleep, as it will get you more meaningfull results.

What is Task

Task is just a construct to help with all forms of Multitasking. It is "agnostic" to how it is run. Get() can be executed asynchronously using ThreadPools. Asynchronously using async and await. Synchronously by just calling RunSynchronously(). Or asynchronously by calling RunSynchronously() from a Thread you manually started. And propably a few other ways, I can not remember yet.

Different kinds of Multitasking

Multithreading can be used to implement Multitasking. Technically we only need Multithreading with CPU bound work, but for the longest time it was the most expedient way to implement MT in general, so we tended to use it. A lot. Especially in cases where it was not nessesary.

Even fully undertanding why we did it, I think we overused to abused Thread based multitasking. And especially with GUIs, Multithreading causes some issues. Still, it was the easier way. So a lot of things, a lot of examples are still designed for Thread Based Multitasking.

Only recently, did we get async and await as alternative. async and await are a way of Multitasking without restorting to Mutlthreading. We always had and still have the option do the work of those two ourself. But that is quite code intensive and prone to error. Those two are easy and resolved reliably between the Compiler and the Runtime. So only now, do we start going back to forms of Threadless Multitasking.

Task.Run:

"Queues the specified work to run on the ThreadPool and returns a task or Task handle for that work." - https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.run

So Run takes a task, and executes it via a ThreadPool - a form of Thread based mutltiasking.

Stelios Giakoumidis
  • 1,670
  • 1
  • 3
  • 16
Christopher
  • 8,956
  • 2
  • 14
  • 31