1

I have this code:

private async void Button_Click(object sender, RoutedEventArgs e)
{
    Task<int> i = LongTaskAsync();    // first break point here
    int k = await i;
    print("Done, i=" + i);
}
private async Task<int> LongTaskAsync()
{
    await System.Threading.Tasks.Task.Delay(5 * 1000); // second break point here
    return 10;
}

When debugging, I knew that LongTaskAsync was running on UI thread. So, why it doesn't block UI and how?

CK01
  • 431
  • 4
  • 9
  • Because it says "async"? – Patrick Hofman Dec 15 '15 at 08:59
  • 4
    How do you know that LongTaskAsync was running on the UI thread? – Domysee Dec 15 '15 at 09:02
  • [If you want the FULL details, start here.](http://codeblog.jonskeet.uk/2011/05/08/eduasync-part-1-introduction/) - but note that the task returned from `LongTaskAsync()` is probably NOT running on the UI thread. – Matthew Watson Dec 15 '15 at 09:15
  • I know that, but How It can do that? I thought the LongTaskAsync should run on another thread so It will not block the UI but debugging result shows that LongTaskAsync was still running on UI thread – CK01 Dec 15 '15 at 09:18
  • 2
    @CK01 what debugging results? The only way you can see that is if place a breakpoint *inside* the `LongTaskAsync` method and checked either the *Parallel Stacks* window or checked the thread combo on the Debug toolbar and saw the main thread. The `await` in `await LongTaskAsync()` ensures that after asynchronous execution completes, the event handler will keep working in the UI thread. PS not only is splitting the task declaration from awaiting unnecessary, it's also a sign that you haven't yet realized that `await` awaits for a task to finish, it doesn't cause it to run asynchronously – Panagiotis Kanavos Dec 15 '15 at 09:28
  • @Domysee: I took a break point inside LongTaskAsync() and compared the thread ID with a breakpoint in Button click. – CK01 Dec 15 '15 at 09:50
  • @CK01 please post the LongTaskAsync method. – Domysee Dec 15 '15 at 09:54
  • @Panagiotis: The asynchronous progress start at the time that we call LongTaskAsync right? I took a break point inside button_click and inside LongTaskAsync and compare their thread ID via Debug Threads Window. They are the same so I think LongTaskAsync is still running on UI thread. – CK01 Dec 15 '15 at 09:58
  • No. It starts when *your code* calls an asynchronous operation. That is, you call an asynchronous framework method, an asynchronous service or REST call, or just call Task.Run. What does `LongTaskAsync` contain? `async` as a keyword or as part of a name doesn't make a method asynchronous automagically – Panagiotis Kanavos Dec 15 '15 at 10:01
  • Full code: http://pastebin.com/ngjvxAFw – CK01 Dec 15 '15 at 10:03
  • try breaking on `return 10;` – default Dec 15 '15 at 10:33
  • @CK01: Check out my [`async` intro](http://blog.stephencleary.com/2012/02/async-and-await.html); it will answer your questions. – Stephen Cleary Dec 15 '15 at 13:31
  • @StephenCleary: Hi Stephen, I've read your article before and still had this problem then I brought it here. You said:"Await examines that awaitable to see if it has already completed; if the awaitable has already completed, then the method just continues running (synchronously, just like a regular method).". UI thread will run Await to examines Delay is completed or not, so who will actually run the Delay code? – CK01 Dec 15 '15 at 16:29
  • 1
    @CK01: `Delay` is a timer. [There is no "Delay code" to "run"](http://blog.stephencleary.com/2013/11/there-is-no-thread.html). – Stephen Cleary Dec 15 '15 at 16:43
  • @StephenCleary: what if it's GetStringAsync()? – CK01 Dec 15 '15 at 16:51
  • 1
    @CK01: True asynchronous code does not have any code to "run" for the majority of its time. E.g., if `GetStringAsync` is downloading some string using an HTTP GET request, then there's no thread just blocked, waiting for the server to respond. The request is sent (immediately, synchronously) and the returned task will be completed when the response arrives. – Stephen Cleary Dec 15 '15 at 19:35
  • @StephenCleary: I've read this another article on your blog, "There is no thread" and It answered my question. Thank you very much! – CK01 Dec 16 '15 at 08:12

5 Answers5

1

Information from MSDN

The method runs synchronously until it reaches its first await expression, at which point the method is suspended until the awaited task is complete.In the meantime, control returns to the caller of the method

1

Function button_click is declared async. This function calls an async function. Calling an async function does not run the code immediately as calling a normal function would do. Instead the code is scheduled as a request to run a Task by one of the threads in a collection of available threads that is known as the thread pool.

After scheduling the task, the caller of the async function continues with the next statements until it reaches the statement to await for the result of the scheduled task. Quite often, the await is immediately after the call to the async function, but it does not have to.

Task<int> longRunningTask = LongTaskAsync();
// because of not awaiting, your thread is free to do other things:
DoSomethingElse();

// now I need the result: await for the longRunningTask
int i = await longRunningTask;
ProcessResult(i);

Whenever one of the threads in the thread pool is free, it will check if a piece of code is scheduled and if so it will run the code. If this thread calls another async function, the task is scheduled etc.

Most async functions will somewhere await for the results of the tasks they scheduled. In fact, your compiler will warn you if you forget to await the task. To enable your callers to await for your async function to complete, your function returns Task or Task<TResult> instead of void or TResult.

The only exception is the event handler: this method can return void. This has the effect that the task will be scheduled, but that the caller cannot await for it.

This caller is your application. The application schedules a task to handle the button clicked event but does not await until this event is finished. Hence your application will remain responsive.

Because button_click is async, your UI thread does not wait for this function to finish. Hence your program could enter this function again before LongTaskAsync is finished. Quite often this is an undesired effect and you should take care that this does not happen, for instance by disabling the button at the entrance of the event function and enable it when the event is finished. Something like this:

private async void Button_Click(object sender, RoutedEventArgs e)
{
    // Prevent entering again before this function is finished:
    Button clickedButton = (Button)sender;
    clickedButton.Enabled = false;

    // process the clicked button:
    Task<int> i = LongTaskAsync();    // first break point here
    int k = await i;
    print("Done, i=" + i);

    // Finished processing, enable the button again:
    clickedButton.Enabled = true;
}
Harald Coppoolse
  • 28,834
  • 7
  • 67
  • 116
0

Because async/await construction is running asynchronously - so it doesn't block UI. In your method "int k = await i;" runs asynchronously. "Use the async modifier to specify that a method, lambda expression, or anonymous method is asynchronous. The method runs synchronously until it reaches its first await expression, at which point the method is suspended until the awaited task is complete. In the meantime, control returns to the caller of the method". That's why your UI is not blocked.

Anton23
  • 2,079
  • 5
  • 15
  • 28
-1

I had the same question when I tried writing Android code. Background task is different from the foreground UI task and they run independently.

AsyncTasks usually uses up threads in the foreground to update UI and do UI related tasks

For example,

AsyncTask(params)-> {
@Override
   protected void onPreExecute() {
      super.onPreExecute();
     ..........
   }

   @Override
   protected String doInBackground(String... params) {
   ...............
   }

   @Override
   protected void onProgressUpdate(Integer... values) {
   ...............
   }

   @Override
   protected void onPostExecute(String result) {
   .................
   }
}

Here, each of the tasks could be handled in its own UI thread without interfering with the background job and when the background job is finished, you could update the UI. In the mean time, you could use some UI animation to keep the UI busy.

  • 1
    but this question is regarding C#. This is not related to Android or how Android works. – default Dec 15 '15 at 09:30
  • If I am not wrong it works the same in c# as well, wouldnt it? – Vyshakh Babji Dec 15 '15 at 09:32
  • I've never heard of multiple UI threads in C#. async await *might* spawn a new thread, then again might not. You write that "AsyncTasks usually uses up threads". I would say that this is context dependent in C#. I have never seen the pattern `AsyncTask(params)-> {` in C# so I can't say if that would work like anything in C#. If you feel that this is relevant, maybe write how this is similar to C#? Or for that matter, write an answer which uses C# constructs? – default Dec 15 '15 at 09:36
  • I am more of a Java/Android developer but have done quite a bit of C# but this post would help you answer your question. Let me know if I am wrong http://stackoverflow.com/questions/5095183/how-would-i-run-an-async-taskt-method-synchronously – Vyshakh Babji Dec 15 '15 at 09:50
  • Yes, you are very wrong. Tasks in C# do not have this interface (they don't need it) and the question isn't about creating tasks anyway, it's about awaiting them to finish. In fact, this code looks more like the 2.0 BackgroundWorker component which is now considered deprecated - try composing two or threee asynchronous steps this way! – Panagiotis Kanavos Dec 15 '15 at 10:06
-1

Your sample is not enough to test the asynchronous behavoiur, I tested the behavior with a console application.

static void Main(string[] args)
{
     string value1 = "First";
     Console.WriteLine(value1);

     GetSecondFromAsync();

     string value3 = "Third";
     Console.WriteLine(value3);

     string value4 = "Fourth";
     Console.WriteLine(value4);

     Console.ReadKey();
}

private static async void GetSecondFromAsync()
{
   await Task.Delay(3000);
   Console.WriteLine("Second");
}

Information from MSDN,

The method runs synchronously until it reaches its first await expression, at which point the method is suspended until the awaited task is complete.In the meantime, control returns to the caller of the method

At the point of first await in GetSecondFromAsync(), only this method is suspended and the control returns to the caller and executes the remaining. So the output will be like

First
Third
Fourth
Second

That means the thread comes in the Main is not blocked, it goes on..

Linoy
  • 1,363
  • 1
  • 14
  • 29
  • Console applications do not have UI threads. And your code *doesn't* show proper asynchronous behaviour anyway - you never await for the result of `GetSecondFromAsync`. It may never execute if something doesn't block the calling thread. That's **not** what happens in asynchronous event handlers – Panagiotis Kanavos Dec 15 '15 at 10:03
  • sorry about UI thread, I updated, But the lines in GetSecondFromAsync() is awaiting right? It would be great if you explain it. – Linoy Dec 15 '15 at 10:15