3

In an attempt to understand async/await I made a little sample WPF application that has one button. When clicked it will do some 'work' :

private async void goButtonClicked(object sender, EventArgs e)
        {

            WhatThreadAmI();
            var task = populateRawData().ConfigureAwait(false);
            WhatThreadAmI();
            BusyIndicator.IsBusy = true;    

            await task;
            WhatThreadAmI(); //this isnt on the main thread - why??
            BusyIndicator.IsBusy = false;

            Console.WriteLine("fin");
        }

The "WhatThreadAmI" simply compares the current thread to the UI thread which I save on initialization.

public bool IsMainThread => uiThread == Thread.CurrentThread;

I expected the output of this to be True - True - True, with the "WhatThreadAmI" call in the populate raw data method to return false.

What actually happens is True - True - False, with the ""WhatThreadAmI" call in the populate raw data method returning true.

I know I must be missing something very fundamental here, but can someone please help me understand what is going on?

samodle
  • 72
  • 7
  • [async is not about threads](http://stackoverflow.com/q/17661428/11683). – GSerg May 22 '16 at 14:43
  • 3
    Even if it was, you do say `.ConfigureAwait(false)` when you create the task, which means "I do not need to resume on the original context." As soon as you `await` that task, your context changes, so no surprise here. The context does not change when you simply create the task but do not await it yet, so the two first `WhatThreadAmI` return `true`. – GSerg May 22 '16 at 14:44
  • makes sense - thank you! – samodle May 22 '16 at 15:08

3 Answers3

4
var task = populateRawData().ConfigureAwait(false);

ConfigureAwait(false) returns a configured task awaiter that does not resume on a captured context. I explain how await captures and resumes on context in detail on my blog; the usage of ConfigureAwait(false) means to not capture and resume on the context. In this case, the "context" is the UI thread. So, the await doesn't resume of the UI thread because the ConfigureAwait(false) is explicitly telling the await that it doesn't need to.

On a side note, the task variable in that code does not contain a Task. It's extremely unusual to have the result of ConfigureAwait in a variable instead of with its await. The following example is equivalent and - I think - expresses more clearly what's going on:

WhatThreadAmI();
var task = populateRawData();
WhatThreadAmI();
BusyIndicator.IsBusy = true;    

await task.ConfigureAwait(false);
WhatThreadAmI(); //this isnt on the main thread - why??
BusyIndicator.IsBusy = false;

Put another way: it's ConfigureAwait, not ConfigureTask. It doesn't change the task at all; ConfigureAwait only makes sense to use with await.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
0

the Task object is an abstraction over the .Net Thread pool. one thread may begin executing code, await a not-finished task, and be resumed from a different thread.

in this way you can maximize the use of your CPU without blocking. threads have no meaning to tasks. an available thread will continue your task execution when awaiting is finished. it may be the thread which executed the task in the past, it may not.

David Haim
  • 25,446
  • 3
  • 44
  • 78
  • 1
    I like this answer because it gets at the heart of a critical detail implied in the question. If you are relying on certain behavior with specific threads then Async Await is not your solution. I would describe Async Await as "Try to parallelize if you can." Use threads "randomly". There is a design and architecture detail here that is not trivial if you are trying to understand it. And the original question is a good question if you are trying to understand. Why it will not be as expected in all environments is a great example that the original question brought to the surface. – Sql Surfer May 22 '16 at 15:20
-1

change ConfigureAwait(false); to ConfigureAwait(true); or just remove it.

Jeff
  • 1