"async await " how to understand it

the code is :

    static void Main(string[] args)
    {
        Test();

        Console.WriteLine("Main Thread Id :{0}", Thread.CurrentThread.ManagedThreadId);

        Console.Read();
    }

    static async void Test()
    {
        Console.WriteLine("before wait Current Thread Id:{0}", Thread.CurrentThread.ManagedThreadId);

        await GetName();

        Console.WriteLine("after wait Current Thread Id:{0}", Thread.CurrentThread.ManagedThreadId);
    }

    static async Task GetName()
    {
        await Task.Run(() =>
          {
              Console.WriteLine("Current Thread Id :{0}", Thread.CurrentThread.ManagedThreadId);
              Console.WriteLine("In antoher thread.....");
          });
    }

the result is :

before wait Current Thread Id:9
Current Thread Id :10
In antoher thread.....
Main Thread Id :9       
after wait Current Thread Id:10  or  sometimes is  after wait Current Thread Id:11

i dont kown why "Main Thread Id" is running fast than "after wait Current Thread Id". and why has three ThreadID.

Answers


Well, you can easily trace what happens.

For the main thread:

  1. Method Test is called
  2. before wait is printed out
  3. A new task is queued on the threadpool, and Test immediately returns back to Main

Up to this point, this is a definite synchronous sequence. The remainder is not deterministic, because in theory, either the rest of Main or the threadpool Task can execute first - in fact, they can (in theory) also interleave, with Main Thread Id being printed out in between of Current thread Id and In another thread.

However, after wait will always be printed after In another thread.

You can already see that there's at least two threads involved - that shouldn't be surprising when you use Task.Run. So where does the third one come from? Well, await doesn't do any magic. There's no instruction for magic in IL. So what it actually does is just schedule a continuation on the Task you're awaiting, with the rest of the method. And since there's no synchronization context to marshal the continuation back on, the continuation will simply be queued to the thread pool again - taking another thread. However, in theory, the continuation may be allowed to run synchronously on the original thread (that is, the one used in Task.Run), although in this case I assume it's simply a reuse of the original thread, rather than synchronous continuation. But you can be entirely sure of one thing - it will never run on the main thread - not unless you explicitly setup a synchronization context and handle the marshalled tasks on the main thread instead of using Console.Read for the final blocking operation.


That is because Test runs asynchronous, you don't wait on the result.

You can do this to wait on the result:

Task t = new Task(Test);

t.Wait();

Also, your async method should actually return an instance of Task, since void is only used for async event handlers (MSDN).


The SynchronizationContext in a console app is a new thread so async/await won't operate as you expect in this environment. Try a WPF application.


Let's desugar your code!

This one stays as it was

static void Main(string[] args)
{
    Test();

    Console.WriteLine("Main Thread Id :{0}", Thread.CurrentThread.ManagedThreadId);

    Console.Read();
}

This is an async method. More specifically, it's an async void method, which are sort of wonky; you'll soon see the difference. As a regular method, it looks like this:

static void Test()
{
    Console.WriteLine("before wait Current Thread Id:{0}", Thread.CurrentThread.ManagedThreadId);

    Task getnameresult = GetName();

    Action<Task> continuation = (task) => {
      Console.WriteLine("after wait Current Thread Id:{0}", Thread.CurrentThread.ManagedThreadId);
    }

    Task continuationresult = getnameresult.ContinueWith(continuation);
    //you are guaranteed that continuation will run on some (unspecified) thread after the taskgetnameresult completes

    return;

    //note that we do nothing with continuationresult, not even return it, so there is no way to know if it has completed.
}

And GetName becomes

static Task GetName()
{
    return Task.Run(() =>
      {
          Console.WriteLine("Current Thread Id :{0}", Thread.CurrentThread.ManagedThreadId);
          Console.WriteLine("In antoher thread.....");
      });
}

The methods that set up your asynchronity here are Task.ContinueWith: https://msdn.microsoft.com/en-us/library/dd270696%28v=vs.110%29.aspx and Task.Run: https://msdn.microsoft.com/en-us/library/hh195051%28v=vs.110%29.aspx

Neither of those methods say much about threads, just that they may be run asynchonously. The runtime can and will schedule them as it pleases. Any knowledge of how this works should be seen as an implementation detail, and you should assume this can differ each time you run the application (and it will be different, as some other answers indicate, in a console app or a WPF application)

So what happens here. Well, first off, Main will call Test(). Test will first print the "before wait Current Thread Id". Then it calls GetName()

That method schedules a an action to run on some unspecified thread in some unspecified time in the future, which will print "Current Thread Id" and "In another thread.....", and returns the task representing that action, returning control back to Test

Test will now create a new task, that will at some other time in the future, but definitely after the task that was returned, print the message "after wait Current Thread ID", and return control to Main. Main will now block its running thread until it reads something from the console.

So let's put it all together. We know for sure that "before wait" is printed first, which is a simple synchronous call.

Then we know that after that, The "Current Thread" line will be printed, and that the the "Main thread" line will be printed, but we made no promises about the order in which they will run, or if they might even run concurrently.

In your situation the runtime determines that it will run the task that prints the "Current Thread" line first. Why? Reasons. SynchronisationContexts. But most importantly, stuff that you shouldn't care about. When you are programming asynchronously, you are giving up some control about what runs first, and what runs next.

Then the same thing goes for the next continuation. There is no intrinsic relation between when "Main Thread Id" is printed and when "After wait" is printed, only that "After wait" must come at some point after the "Current Thread" line is printed.

By doing things asynchronously, you are telling the runtime you don't care in which order things run. Trying to reason about which thing will run before which other thing after you told the language that you don't particularly care about which order they run in borders on folly.


Need Your Help

Getting a RuntimeException : Datasource user is null ? when updating User model in Play Framework 2.2.1

java playframework-2.2

I am currently trying to enhance the To-Do List tutorial from Play framework's website. I've added a login form, (and of course a User class acting as a model), and it works perfectly well. I also ...