Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

async - stay on the current thread?

I've read Eric lippert's article about async , and about confusions people had with async keyword. he said :

it (async) means “this method contains control flow that involves awaiting asynchronous operations and will therefore be rewritten by the compiler into continuation passing style to ensure that the asynchronous operations can resume this method at the right spot.” The whole point of async methods it that you stay on the current thread as much as possible

I don't understand this. If I execute an asynchronous method (Task) and it runs , it surely runs on another thread.

Moreover , If I write a method uses await , (imho) it releases the normal control flow , and code is refactored alike "ContinueWith" later , on another thread.

I tested it with (console) :

/*1*/   public void StartChain()
/*2*/   {
/*3*/           var a = FuncA();
/*4*/           Console.WriteLine(a.Result);
/*5*/   }
/*6*/   
/*7*/   public async Task < int > FuncA()
/*8*/   {
/*9*/           Console.WriteLine("A--" + Thread.CurrentThread.ManagedThreadId);
/*10*/           var t = await FuncB();
/*11*/           Console.WriteLine("B--" + Thread.CurrentThread.ManagedThreadId);
/*12*/           return t;
/*13*/   }
/*14*/   
/*15*/   public async Task < int > FuncB()
/*16*/   {
/*17*/           Console.WriteLine("C--" + Thread.CurrentThread.ManagedThreadId);
/*18*/           await Task.Delay(2000);
/*19*/           Console.WriteLine("D--" + Thread.CurrentThread.ManagedThreadId);
/*20*/           return 999;
/*21*/   }
/*22*/   
/*23*/   void Main()
/*24*/   {
/*25*/           StartChain();
/*26*/   }
/*27*/   

the result is :

A--7
C--7
D--17         <-----D  and B are on different thread
B--17
999

So what did Eric mean by saying "stay on the current thread"?

edit 1:

in asp.net it also return differnt thread ID.

public async Task<int> FuncA()
{
    Response.Write("<br/>C----" + Thread.CurrentThread.ManagedThreadId);
    var t = await FuncB();
    Response.Write("<br/>D----" + Thread.CurrentThread.ManagedThreadId);
    return t;
}

public async Task<int> FuncB()
{
    Response.Write("<br/>E----" + Thread.CurrentThread.ManagedThreadId);
    await Task.Delay(2000);
    Response.Write("<br/>F----" + Thread.CurrentThread.ManagedThreadId);
    return 999;
}



protected async void Page_Load(object sender, EventArgs e)
{
    Response.Write("<br/>A----" + Thread.CurrentThread.ManagedThreadId);
    var a=await FuncA();
    Response.Write("<br/>B----" + Thread.CurrentThread.ManagedThreadId);

}

A----8
C----8
E----8
F----9
D----9
B----9

edit 2

(after getting an answer)

it seems that thread is served only at GUI apps :. I run this code at winform

  public async Task<int> FuncA()
        {
            textBox1.Text +=Environment.NewLine+ "\nC----" + Thread.CurrentThread.ManagedThreadId;
            var t = await FuncB();
            textBox1.Text += Environment.NewLine + "\nD----" + Thread.CurrentThread.ManagedThreadId;
            return t;
        }

        public async Task<int> FuncB()
        {
            textBox1.Text += Environment.NewLine + "\nE----" + Thread.CurrentThread.ManagedThreadId;
            await Task.Delay(2000);
            textBox1.Text += Environment.NewLine + "\nF----" + Thread.CurrentThread.ManagedThreadId;
            return 999;
        }




        private async void Form1_Load(object sender, EventArgs e)
        {
            textBox1.Text += Environment.NewLine + "\nA----" + Thread.CurrentThread.ManagedThreadId;
            var a = await FuncA();
            textBox1.Text += Environment.NewLine + "\nB----" + Thread.CurrentThread.ManagedThreadId;
        }

enter image description here

like image 991
Royi Namir Avatar asked Jul 15 '13 18:07

Royi Namir


People also ask

Does async await run on the main thread?

You create your new async Task from your UI code on the main thread — and now this happens! Remember, you learned that every use of await is a suspension point, and your code might resume on a different thread. The first piece of your code runs on the main thread because the task initially runs on the main actor.

Does async block main thread?

When you await in an async function, you don't block that thread. Rather, the Swift compiler rewrites your code to save its state in memory associated with the task and returns control to the runtime. That thread is then free to continue doing other work.

Does await stop the main thread?

Because await is only valid inside async functions and modules, which themselves are asynchronous and return promises, the await expression never blocks the main thread and only defers execution of code that actually depends on the result, i.e. anything after the await expression.

Can async method run on the UI thread?

@pm100 The method they're calling is an asyncrhonous method that interacts with the UI, and as such needs to be run on the UI thread. It's incorrect to run it in a non-UI thread. It will never work if you do that. It needs to be run in the UI thread.


2 Answers

If I execute an asynchronous method and it runs, it surely runs on another thread.

No, it typically runs on another thread. It does not surely run on another thread.

Stop thinking about threads for a moment and think about the nature of asynchrony. The nature of asynchrony is:

  • I've got some workflow that I am currently executing.
  • I can't proceed in this workflow until I get information X.
  • I'm going to do something else until I get information X.
  • At some point in the future, once I have X, I'm going to come back to where I left off in my workflow and continue.

Suppose you're doing your taxes and in the middle of this complicated workflow you have a large addition to perform. You can perform a few operations then remember where you are, and go have lunch. Then come back and perform a few more operations, then remember where you are, and feed the cat. Then come back and perform a few more operations, then remember where you are, and wash the dishes. Then finish off the calculations, and resume where you left off in your workflow.

That's an asynchronous calculation but it only needed a single worker to do it. Having multiple workers is just a particularly convenient way to do asynchrony, it is not a requirement.

like image 186
Eric Lippert Avatar answered Sep 20 '22 18:09

Eric Lippert


The async/await support was added to help programmers write GUIs that don't freeze. Particularly useful in Store apps, and the core reason it was added to C# v5, WinRT is a pretty unfriendly api that has many asynchronous methods.

The "stay on the same thread" scenario is very important in a GUI app, required because a GUI isn't thread-safe. It does however require a dispatcher loop (aka Application.Run), the only way to get asynchronous code to resume on the same thread. That loop is the core solution to the producer-consumer problem.

Clearly your program doesn't have one, looks a lot like a console mode app. You therefore don't get this behavior, it resumes on a worker thread.

Not much of a problem, you don't actually need it to resume on the same thread since a console is thread-safe anyway. Well, mostly, not counting the lock that was added in .NET 4.5 when you ask for input. Which of course also means that you don't have a heckofalot of use for async/await either, a Task works fine as well.

like image 23
Hans Passant Avatar answered Sep 17 '22 18:09

Hans Passant