How does the control flow stay in scope of the async method after it has returned the result? Is this similar to variable closure?

In the code below, 42 is printed to the Console before Almost Done. Since 42 is already returned, how is the control still in AsyncCheck

? I understand that if the task did not complete with "pending" it will return the control, but here I have an explicit return statement, so I am having problems with that.

async static Task<int> AsyncCheck()
{
   await Task.Factory.StartNew( async () => 
    {
        Console.WriteLine("Start awesome work");
        await Task.Delay(1000);
        Console.WriteLine("Almost done");
        await Task.Delay(1000);
        Console.WriteLine("Ok, done!");
    });

   return 42;
}

async static void AsyncCall()
{
    int result = await AsyncCheck();
    Console.WriteLine(result);
}

      

I am using "async void" just to test this sample.

+3


source to share


2 answers


You don't have to use Task.Factory.StartNew

with async-await

. You must use Task.Run

. Task.Factory.StartNew

came in .Net 4.0 without async-await

. You can still use it, but if you don't have a good reason, there Task.Run

is a safer alternative.

Instead of returning a, Task

it returns a Task<Task>

, which needs to be expanded before waiting:

Task<Task> task = Task.Factory.StartNew( async () => 
{
    Console.WriteLine("Start awesome work");
    await Task.Delay(1000);
    Console.WriteLine("Almost done");
    await Task.Delay(1000);
    Console.WriteLine("Ok, done!");
});
Task unwrappedTask = task.Unwrap();
await unwrappedTask

      



Without it, you can't wait for you to finish. Everything you do uses a thread ThreadPool

to run this asynchronous method.

Basically you are executing your lambda in parallel on a different thread while the code flow continues. This is why you see "42" to "Almost done"

+4


source


Let desagar! You

async static Task<int> AsyncCheck()
{
   await Task.Factory.StartNew( async () => 
    {
        Console.WriteLine("Start awesome work");
        await Task.Delay(1000);
        Console.WriteLine("Almost done");
        await Task.Delay(1000);
        Console.WriteLine("Ok, done!");
    });

   return 42;
}

async static void AsyncCall()
{
    int result = await AsyncCheck();
    Console.WriteLine(result);
}

      

The compiler includes the following:



static Task<int> AsyncCheck()
{
   Task t = Task.Factory.StartNew( () => 
    {
        Console.WriteLine("Start awesome work");
        Task d1 = Task.Delay(1000);
        d1.ContinueWith(t1 => {
          Console.WriteLine("Almost done");
          Task t2 = Task.Delay(1000);
          t2.ContinueWith( tt2 => {
            Console.WriteLine("Ok, done!");
          }
        }
    });
   //no dependecies on t, so just return straight away
   return Task.FromResult(42);
}

static void AsyncCall()
{
    Task<int> result = AsyncCheck();
    result.ContinueWith( t => {
      Console.WriteLine(t.Result);
    })
}

      

ContinueWith

will run its body upon completion of the task. This should make the control flow transparent.

+1


source







All Articles