Cast a Task<T> to a T - c#

How do I cast an Task<IEnumerable<IMapLocationItem>> to an IEnumerable<IMapLocationItem>?
Here is my code that gets a Task<IEnumerable<IMapLocationItem>>:
public async Task<IEnumerable<IMapLocationItem>> GetAllMapLocationItemsFromUserName(string userName)
{
IEnumerable<IMapLocationItem> mapLocationImageGalleries = await db.mapLocationImageGalleries.Where(m => m.userName.Equals(userName)).ToListAsync();
IEnumerable<IMapLocationItem> mapLocationVideoGalleries = await db.mapLocationVideoGalleries.Where(m => m.userName.Equals(userName)).ToListAsync();
IEnumerable<IMapLocationItem> mapLocationItemsCombined = mapLocationImageGalleries.Concat(mapLocationVideoGalleries);
return mapLocationItemsCombined;
}
I can use the keyword .Result, but I have read somewhere that this prevents the async task from being asynchronous, and this method takes a very long time to finish when using the Result keyword.
How is the best way to cast a Task<IEnumerable<IMapLocationItem>> to an IEnumerable<IMapLocationItem>?
Thanks in advance

From the MSDN website http://msdn.microsoft.com/en-us/library/hh191443.aspx
// Signature specifies Task<TResult>
async Task<int> TaskOfTResult_MethodAsync()
{
int hours;
// . . .
// Return statement specifies an integer result.
return hours;
}
// Calls to TaskOfTResult_MethodAsync
Task<int> returnedTaskTResult = TaskOfTResult_MethodAsync();
int intResult = await returnedTaskTResult;
// or, in a single statement
int intResult = await TaskOfTResult_MethodAsync();
You cannot cast a task to the result. That defeats the purpose of async. What the code above does is calls the function asynchronously, then later in the code requests the result by calling "await". This will wait for the asnyc function to finish (will block the code until that is done) until the result is ready.
The issue is that you cannot predict when something will be done when it is running asynchronously, so instead we tell the Task when we are finally ready to wait for the result. It could be done right away or in 5 minutes. Await will wait for as long as it is necessary for the function to finish.

You can't cast a Task<T> to T, they represent different things. A Task<T> represents a task that in the future will eventually return a T. If you want to get that result of a task you have several options (assuming t = Task<T>)
t.Result blocks the execution until the result is available. This has the unfortunate side effect that it might deadlock in some cases, most notably when combined with async methods in UI threads.
await t schedules a continuation and runs it once the result is available. Only available with C# 5 and .NET 4.5 or .NET 4 with the Microsoft.Bcl.Async library.
t.ContinueWith schedules a continuation and runs it once the result is available.
In general I prefer the use of await or ContinueWith, but sometimes using Result is the easiest, especially if you have access to the source of both the calling and called method and thus can ensure that no deadlock occurs.

Related

KeyVaultClient hangs at GetSecretAsync [duplicate]

I have a multi-tier .Net 4.5 application calling a method using C#'s new async and await keywords that just hangs and I can't see why.
At the bottom I have an async method that extents our database utility OurDBConn (basically a wrapper for the underlying DBConnection and DBCommand objects):
public static async Task<T> ExecuteAsync<T>(this OurDBConn dataSource, Func<OurDBConn, T> function)
{
string connectionString = dataSource.ConnectionString;
// Start the SQL and pass back to the caller until finished
T result = await Task.Run(
() =>
{
// Copy the SQL connection so that we don't get two commands running at the same time on the same open connection
using (var ds = new OurDBConn(connectionString))
{
return function(ds);
}
});
return result;
}
Then I have a mid level async method that calls this to get some slow running totals:
public static async Task<ResultClass> GetTotalAsync( ... )
{
var result = await this.DBConnection.ExecuteAsync<ResultClass>(
ds => ds.Execute("select slow running data into result"));
return result;
}
Finally I have a UI method (an MVC action) that runs synchronously:
Task<ResultClass> asyncTask = midLevelClass.GetTotalAsync(...);
// do other stuff that takes a few seconds
ResultClass slowTotal = asyncTask.Result;
The problem is that it hangs on that last line forever. It does the same thing if I call asyncTask.Wait(). If I run the slow SQL method directly it takes about 4 seconds.
The behaviour I'm expecting is that when it gets to asyncTask.Result, if it's not finished it should wait until it is, and once it is it should return the result.
If I step through with a debugger the SQL statement completes and the lambda function finishes, but the return result; line of GetTotalAsync is never reached.
Any idea what I'm doing wrong?
Any suggestions to where I need to investigate in order to fix this?
Could this be a deadlock somewhere, and if so is there any direct way to find it?
Yep, that's a deadlock all right. And a common mistake with the TPL, so don't feel bad.
When you write await foo, the runtime, by default, schedules the continuation of the function on the same SynchronizationContext that the method started on. In English, let's say you called your ExecuteAsync from the UI thread. Your query runs on the threadpool thread (because you called Task.Run), but you then await the result. This means that the runtime will schedule your "return result;" line to run back on the UI thread, rather than scheduling it back to the threadpool.
So how does this deadlock? Imagine you just have this code:
var task = dataSource.ExecuteAsync(_ => 42);
var result = task.Result;
So the first line kicks off the asynchronous work. The second line then blocks the UI thread. So when the runtime wants to run the "return result" line back on the UI thread, it can't do that until the Result completes. But of course, the Result can't be given until the return happens. Deadlock.
This illustrates a key rule of using the TPL: when you use .Result on a UI thread (or some other fancy sync context), you must be careful to ensure that nothing that Task is dependent upon is scheduled to the UI thread. Or else evilness happens.
So what do you do? Option #1 is use await everywhere, but as you said that's already not an option. Second option which is available for you is to simply stop using await. You can rewrite your two functions to:
public static Task<T> ExecuteAsync<T>(this OurDBConn dataSource, Func<OurDBConn, T> function)
{
string connectionString = dataSource.ConnectionString;
// Start the SQL and pass back to the caller until finished
return Task.Run(
() =>
{
// Copy the SQL connection so that we don't get two commands running at the same time on the same open connection
using (var ds = new OurDBConn(connectionString))
{
return function(ds);
}
});
}
public static Task<ResultClass> GetTotalAsync( ... )
{
return this.DBConnection.ExecuteAsync<ResultClass>(
ds => ds.Execute("select slow running data into result"));
}
What's the difference? There's now no awaiting anywhere, so nothing being implicitly scheduled to the UI thread. For simple methods like these that have a single return, there's no point in doing an "var result = await...; return result" pattern; just remove the async modifier and pass the task object around directly. It's less overhead, if nothing else.
Option #3 is to specify that you don't want your awaits to schedule back to the UI thread, but just schedule to the thread pool. You do this with the ConfigureAwait method, like so:
public static async Task<ResultClass> GetTotalAsync( ... )
{
var resultTask = this.DBConnection.ExecuteAsync<ResultClass>(
ds => return ds.Execute("select slow running data into result");
return await resultTask.ConfigureAwait(false);
}
Awaiting a task normally would schedule to the UI thread if you're on it; awaiting the result of ContinueAwait will ignore whatever context you are on, and always schedule to the threadpool. The downside of this is you have to sprinkle this everywhere in all functions your .Result depends on, because any missed .ConfigureAwait might be the cause of another deadlock.
This is the classic mixed-async deadlock scenario, as I describe on my blog. Jason described it well: by default, a "context" is saved at every await and used to continue the async method. This "context" is the current SynchronizationContext unless it it null, in which case it is the current TaskScheduler. When the async method attempts to continue, it first re-enters the captured "context" (in this case, an ASP.NET SynchronizationContext). The ASP.NET SynchronizationContext only permits one thread in the context at a time, and there is already a thread in the context - the thread blocked on Task.Result.
There are two guidelines that will avoid this deadlock:
Use async all the way down. You mention that you "can't" do this, but I'm not sure why not. ASP.NET MVC on .NET 4.5 can certainly support async actions, and it's not a difficult change to make.
Use ConfigureAwait(continueOnCapturedContext: false) as much as possible. This overrides the default behavior of resuming on the captured context.
I was in the same deadlock situation but in my case calling an async method from a sync method, what works for me was:
private static SiteMetadataCacheItem GetCachedItem()
{
TenantService TS = new TenantService(); // my service datacontext
var CachedItem = Task.Run(async ()=>
await TS.GetTenantDataAsync(TenantIdValue)
).Result; // dont deadlock anymore
}
is this a good approach, any idea?
Just to add to the accepted answer (not enough rep to comment), I had this issue arise when blocking using task.Result, event though every await below it had ConfigureAwait(false), as in this example:
public Foo GetFooSynchronous()
{
var foo = new Foo();
foo.Info = GetInfoAsync.Result; // often deadlocks in ASP.NET
return foo;
}
private async Task<string> GetInfoAsync()
{
return await ExternalLibraryStringAsync().ConfigureAwait(false);
}
The issue actually lay with the external library code. The async library method tried to continue in the calling sync context, no matter how I configured the await, leading to deadlock.
Thus, the answer was to roll my own version of the external library code ExternalLibraryStringAsync, so that it would have the desired continuation properties.
wrong answer for historical purposes
After much pain and anguish, I found the solution buried in this blog post (Ctrl-f for 'deadlock'). It revolves around using task.ContinueWith, instead of the bare task.Result.
Previously deadlocking example:
public Foo GetFooSynchronous()
{
var foo = new Foo();
foo.Info = GetInfoAsync.Result; // often deadlocks in ASP.NET
return foo;
}
private async Task<string> GetInfoAsync()
{
return await ExternalLibraryStringAsync().ConfigureAwait(false);
}
Avoid the deadlock like this:
public Foo GetFooSynchronous
{
var foo = new Foo();
GetInfoAsync() // ContinueWith doesn't run until the task is complete
.ContinueWith(task => foo.Info = task.Result);
return foo;
}
private async Task<string> GetInfoAsync
{
return await ExternalLibraryStringAsync().ConfigureAwait(false);
}
quick answer :
change this line
ResultClass slowTotal = asyncTask.Result;
to
ResultClass slowTotal = await asyncTask;
why? you should not use .result to get the result of tasks inside most applications except console applications if you do so your program will hang when it gets there
you can also try the below code if you want to use .Result
ResultClass slowTotal = Task.Run(async ()=>await asyncTask).Result;

Why do I get a warning when trying to run an async method on another thread?

I have an async method, which calls another async method, however, I want it run on a separate thread in parallel:
public async Task<Page> ServePage() {
Task.Run(() => DoThings(10)); // warning here
// ... other code
return new Page();
}
public async Task DoThings(int foo) {
// stuff
}
The warning states:
Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
That is, in fact, what I am trying to do. Why am I getting a compiler warning? Is the syntax of Task.Run incorrect?
TL;DR
The reason why you get the warning is because
Task.Run(() => DoThings(10)); // warning here
returns a Task, and since your ServePage method is marked as async, the compiler believes that you should await the result of the Task
Detail
You're mixing two very different paradigms, which coincidentally both involve Task, viz:
Task.Run(), which is generally useful for parallelizing CPU bound work by utilizing multiple cores which may be available
async / await, which is useful for waiting for I/O bound operations to complete, without blocking (wasting) a thread.
So for instance, if you wanted to do 3 x CPU bound operations concurrently, and since Task.Run returns a Task, what you could do is:
public Page ServePage() // If we are CPU bound, there's no point decorating this as async
{
var taskX = Task.Run(() => CalculateMeaningOfLife()); // Start taskX
var taskY = Task.Run(() => CalculateJonSkeetsIQ()); // Start taskY
var z = DoMoreHeavyLiftingOnCurrentThread();
Task.WaitAll(taskX, taskY); // Wait for X and Y - the Task equivalent of `Thread.Join`
// Return a final object comprising data from the work done on all three tasks
return new Page(taskX.Result, taskY.Result, z);
}
The above is likely to utilise up to three threads, which could do the CPU bound work concurrently if there are sufficient cores to do so. Note however that using multiple threads concurrently will reduce the scalability of your system, since fewer simultaneous pages can be served without context switching.
This is in contrast to async / await, which is generally used to free up threads while waiting for I/O bound calls to complete. Async is commonly used in Api and Web apps to increase scalability as the thread is released for other work while the IO bound work happens.
Assuming DoThings is indeed I/O bound, we can do something like:
public async Task<string> DoThings(int foo) {
var result = await SomeAsyncIo(foo);
return "done!";
}
Async work can also be done in parallel:
public async Task<Page> ServePage() {
var task1 = DoThings(123); // Kick off Task 1
var task2 = DoThings(234); // Kick off Task 2 in parallel with task 1
await Task.WhenAll(task1, task2); // Wait for both tasks to finish, while releasing this thread
return new Page(task1.Result, task2.Result); // Return a result with data from both tasks
}
If the I/O bound work takes a reasonable amount of time, there's a good chance there's a point during the await Task.WhenAll when ZERO threads are actually running - See Stephen Cleary's article.
There's a 3rd, but very dangerous option, which is fire and forget. Since method DoThings is already marked as async, it already returns a Task, so there's no need at all to use Task.Run at all. Fire and forget would look as follows:
public Page ServePage() // No async
{
#pragma warning disable 4014 // warning is suppresed by the Pragma
DoThings(10); // Kick off DoThings but don't wait for it to complete.
#pragma warning enable 4014
// ... other code
return new Page();
}
As per #JohnWu's comment, the 'fire and forget' approach is dangerous and usually indicates a design smell. More on this here and here
Edit
Re:
there is nuance to this that escapes me over and over, such as that calling an async method that returns Task from a synchronous method fires-and-forgets execution of the method. (That's the very last code sample.) Am I understanding that correctly?
It's a bit difficult to explain, but irrespective of whether called with, or without the await keyword, any synchronous code in an invoked async method before the first await will be executed on the caller's thread, unless we resort to hammers like Task.Run.
Perhaps this example might help the understanding (note that we're deliberately using synchronous Thread.Sleep and not await Task.Delay to simulate CPU bound work and introduce latency which can be observed)
public async Task<Page> ServePage()
{
// Launched from this same thread,
// returns after ~2 seconds (i.e. hits both sleeps)
// continuation printed.
await DoThings(10);
#pragma warning disable 4014
// Launched from this same thread,
// returns after ~1 second (i.e. hits first sleep only)
// continuation not yet printed
DoThings(10);
// Task likely to be scheduled on a second thread
// will return within few milliseconds (i.e. not blocked by any sleeps)
Task.Run(() => DoThings(10));
// Task likely to be scheduled on a second thread
// will return after 2 seconds, although caller's thread will be released during the await
// Generally a waste of a thread unless also doing CPU bound work on current thread, or unless we want to release the calling thread.
await Task.Run(() => DoThings());
// Redundant state machine, returns after 2 seconds
// see return Task vs async return await Task https://stackoverflow.com/questions/19098143
await Task.Run(async () => await DoThings());
}
public async Task<string> DoThings(int foo) {
Thread.Sleep(1000);
var result = await SomeAsyncIo(foo);
Trace.WriteLine("Continuation!");
Thread.Sleep(1000);
return "done!";
}
There's one other important point to note - in most cases, there are no guarantees that the continuation code AFTER an await will be executed on the same thread as that before the await. The continuation code is re-written by the compiler into a Task, and the continuation task will be scheduled on the thread pool.
This
Task.Run(() => DoThings(10));
will run your separate task "in parallel", meaning that another thread will run this task. The thread that entered this method will then continue execution of the next statement.
What you're doing here is allowed. That's why it's a warning. (I'm assuming that the rest of the method, not shown, returns a Page.)
The message is warning you that because the other task is executing on another thread, it could execute before, after, or at the same time as the other code in the method. This method no longer "knows" what the task is doing or when it's finished.
It's saying, in other words:
Don't assume that because this line of code appears before other lines of code in the method that it's going to execute before they do. If something needs to execute after this task finishes, await it before doing the next thing.
As an example:
public int DoSomething()
{
var x = 1;
Task.Run(() => x++);
return x;
}
What does this return? It depends. It could return 1 or 2. It may return either before or after x is incremented. If you care whether or not x has been incremented then this is bad. If your task does something that doesn't matter to the rest of the method and you don't care whether the task even finishes before or after the rest of the method, then this warning wouldn't matter to you. If you do care then it's important and you'd want to await the task.
All versions bellow are valid, warning free, and more or less equivalent:
public Task ServePage1()
{
return Task.Run(async () => await DoThings(10));
}
public async Task ServePage2()
{
await Task.Run(async () => await DoThings(10));
}
public Task ServePage3()
{
return Task.Run(() => DoThings(10));
}
public async Task ServePage4()
{
await Task.Run(() => DoThings(10));
}
In general you should not fire-and-forget tasks by ignoring the return value of Task.Run. If you do that the compiler will issue a warning, because it is rarely intentional.
When you call an async method that returns a Task, you can do two things:
Await the Task, which returns control to the caller immediately, and resumes when the Task completes (with or without a return value). This is also where you can trap any exceptions that might have happened when executing the Task.
Fire and Forget. This is when you start a Task and completely cut it loose - including the ability to trap exceptions. Most importantly, unless awaited, control execution will proceed beyond the call and can cause unintended state corruption issues.
You did #2 in your code. While technically allowed, because of the reasons cited above, the compiler warned you.
Question for you though - if you really just wanted to fire and forget, why did you need to create that explicit Task? You could just have called DoThings(10) directly, couldn't you? Unless I am missing something in the code that is not visible to me.
So - couldn't you have done just this?
public async Task<Page> ServePage() {
DoThings(10);
}

When does await examine the awaitable?

Let me quote the code snippet and its relevant explanation from Async and Await article written by Stephen Cleary as follows.
public async Task DoSomethingAsync()
{
// In the Real World, we would actually do something...
// For this example, we're just going to (asynchronously) wait 100ms.
await Task.Delay(100);
}
The beginning of an async method is executed just like any other method. That is, it runs synchronously until it hits an “await” (or throws an exception).
The “await” keyword is where things can get asynchronous. Await is like a unary operator: it takes a single argument, an awaitable (an “awaitable” is an asynchronous operation). 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).
If “await” sees that the awaitable has not completed, then it acts asynchronously. It tells the awaitable to run the remainder of the method when it completes, and then returns from the async method.
To accommodate more general case, let me define the code snippet as follows.
public async Task DoSomethingAsync()
{
AAA();
await BBBAsync;
CCC();
}
where BBBAsync is the only "awaitable" operation or method.
For the sake of simplicity, let's assume we execute DoSomethingAsync from within the main app thread. So in my understanding,
AAA() will always be executed synchronously.
BBBAsync() will always be executed asynchronously no matter how DoSomethingAsync() is invoked.
CCC() may be executed either asynchronously or synchronously depending on the result of the await operator determines.
It seems to me, the await operator always find the awaitable has not been completed yet because await always comes before BBBAsync() starts.
Question
When does await examine the awaitable?
It sounds like you think async makes method asynchronous which is not the case. Async only changes how code for the method is generated, but by itself it does not add any asynchronous steps nor run the method on another thread. See many questions like What is the difference between asynchronous programming and multithreading?.
await examines awaitable at the same time irrespective whether it completed yet or not - method like BBBAsync() will eventually return Task that may be either completed or not, but method will return.
Here is an example of task completing synchronously:
async Task BBBAsync() // ignore CS1998 as we don't want `await` inside
{
return;
}
// alternatively the same behavior just returning completed Task
// with FromResult or Task.CompletedTask for 4.6 and later
Task BB2Async()
{
return Task.FromResult(0);
}
You can await such task just fine, but this BBBAsync will complete synchronously and await will pick synchronous branch to continue.
More realistic sample would be returning recently cached value synchronously. This code will generate full state machine for asynchronous execution, but every other call will return synchronously thus making corresponding await to continue synchronously too.
async Task<int> GetValueAsync()
{
if (canReturnCached)
{
canReturnCached = false;
return cachedValue;
}
cachedValue = await SlowRemoteCallForValueAsync();
canReturnCached = true;
return cachedValue;
}

multiple awaits vs Task.WaitAll - equivalent?

In terms of performance, will these 2 methods run GetAllWidgets() and GetAllFoos() in parallel?
Is there any reason to use one over the other? There seems to be a lot happening behind the scenes with the compiler so I don't find it clear.
============= MethodA: Using multiple awaits ======================
public async Task<IHttpActionResult> MethodA()
{
var customer = new Customer();
customer.Widgets = await _widgetService.GetAllWidgets();
customer.Foos = await _fooService.GetAllFoos();
return Ok(customer);
}
=============== MethodB: Using Task.WaitAll =====================
public async Task<IHttpActionResult> MethodB()
{
var customer = new Customer();
var getAllWidgetsTask = _widgetService.GetAllWidgets();
var getAllFoosTask = _fooService.GetAllFos();
Task.WaitAll(new List[] {getAllWidgetsTask, getAllFoosTask});
customer.Widgets = getAllWidgetsTask.Result;
customer.Foos = getAllFoosTask.Result;
return Ok(customer);
}
=====================================
The first option will not execute the two operations concurrently. It will execute the first and await its completion, and only then the second.
The second option will execute both concurrently but will wait for them synchronously (i.e. while blocking a thread).
You shouldn't use both options since the first completes slower than the second and the second blocks a thread without need.
You should wait for both operations asynchronously with Task.WhenAll:
public async Task<IHttpActionResult> MethodB()
{
var customer = new Customer();
var getAllWidgetsTask = _widgetService.GetAllWidgets();
var getAllFoosTask = _fooService.GetAllFos();
await Task.WhenAll(getAllWidgetsTask, getAllFoosTask);
customer.Widgets = await getAllWidgetsTask;
customer.Foos = await getAllFoosTask;
return Ok(customer);
}
Note that after Task.WhenAll completed both tasks already completed so awaiting them completes immediately.
Short answer: No.
Task.WaitAll is blocking, await returns the task as soon as it is encountered and registers the remaining part of the function and continuation.
The "bulk" waiting method you were looking for is Task.WhenAll that actually creates a new Task that finishes when all tasks that were handed to the function are done.
Like so: await Task.WhenAll({getAllWidgetsTask, getAllFoosTask});
That is for the blocking matter.
Also your first function does not execute both functions parallel. To get this working with await you'd have to write something like this:
var widgetsTask = _widgetService.GetAllWidgets();
var foosTask = _fooService.GetAllWidgets();
customer.Widgets = await widgetsTask;
customer.Foos = await foosTask;
This will make the first example to act very similar to the Task.WhenAll method.
As an addition to what #i3arnon said. You will see that when you use await you are forced to have to declare the enclosing method as async, but with waitAll you don't. That should tell you that there is more to it than what the main answer says. Here it is:
WaitAll will block until the given tasks finish, it does not pass control back to the caller while those tasks are running. Also as mentioned, the tasks are run asynchronous to themselves, not to the caller.
Await will not block the caller thread, it will however suspend the execution of the code below it, but while the task is running, control is returned back to the caller. For the fact that control is returned back to the caller (the called method is running async), you have to mark the method as async.
Hopefully the difference is clear. Cheers
Only your second option will run them in parallel. Your first will wait on each call in sequence.
As soon as you invoke the async method it will start executing. Whether it will execute on the current thread (and thus run synchronously) or it will run async is not possible to determine.
Thus, in your first example the first method will start doing work, but then you artificially stops the flow of the code with the await. And thus the second method will not be invoked before the first is done executing.
The second example invokes both methods without stopping the flow with an await. Thus they will potentially run in parallel if the methods are asynchronous.

How Async and Await works

I am trying to understand how Async and Await works. How control travel in the program. Here is the code which I was trying to understand.
public async Task MyMethod()
{
Task<int> longRunningTask = LongRunningOperation();
//indeed you can do independent to the int result work here
MySynchronousMethod();
//and now we call await on the task
int result = await longRunningTask;
//use the result
Console.WriteLine(result);
}
public async Task<int> LongRunningOperation() // assume we return an int from this long running operation
{
await Task.Delay(5000); //5 seconds delay
return 1;
}
private void Button_Click_3(object sender, RoutedEventArgs e)
{
MyMethod();
}
When button click occur then MyMethod() will be called and from the MyMethod LongRunningOperation() will be called and it take 5 sec to complete. so my question is
What is the meaning of this line
Task longRunningTask = LongRunningOperation();
what this does int result = await longRunningTask;
The above line could be committed and one line we can construct like
Task<int> longRunningTask = await LongRunningOperation();
or
int result = await longRunningTask;
Please can someone explain to me the above code which is not clear to me.
1) if the longRunningOperation hasn't finished and is still running, MyMethod() will return to its calling method, thus the main thread doesn't get blocked. When the longRunningOperation is done then a thread from the ThreadPool (can be any thread) will return to MyMethod() at its previous state and continue execution (in this case printing the result to the console).
A second case would be that the longRunningOperation has already finished its execution and the result is available. When reaching the await longRunningOperation the compiler knows that it has the result and will keep on executing code on the very same thread. (in this case printing result to console).
point 1 is not at all clear to me like the statement "if the longRunningOperation hasn't finished and is still running, MyMethod() will return to its calling method"
if possible explain the point one in more detail. thanks
I've been taught about it in the following fashion, I found it to be quite a clear and concise explanation:
//this is pseudocode
async Method()
{
code;
code;
await something;
moreCode;
}
When Method is invoked, it executes its contents (code; lines) up to await something;. At that point, something; is fired and the method ends like a return; was there.
something; does what it needs to and then returns.
When something; returns, execution gets back to Method and proceeds from the await onward, executing moreCode;
In a even more schematic fashion, here's what happens:
Method is invoked
code; is executed
something; is executed, flow goes back to the point where Method was invoked
execution goes on with what comes after the Method invocation
when something; returns, flow returns inside Method
moreCode; is executed and Method itself ends (yes, there could be something else await-ing on it too, and so on and so forth)
I have an async intro on my blog that you may find helpful.
This code:
int result = await LongRunningOperation();
is essentially the same as this code:
Task<int> resultTask = LongRunningOperation();
int result = await resultTask;
So, yes, LongRunningOperation is invoked directly by that method.
When the await operator is passed an already-completed task, it will extract the result and continue executing the method (synchronously).
When the await operator is passed an incomplete task (e.g., the task returned by LongRunningOperation will not be complete), then by default await will capture the current context and return an incomplete task from the method.
Later, when the await task completes, the remainder of the method is scheduled to run in that context.
This "context" is SynchronizationContext.Current unless it is null, in which case it is TaskScheduler.Current. If you're running this in a Console app, then the context is usually the thread pool context, so the async method will resume executing on a thread pool thread. However, if you execute the same method on a UI thread, then the context is a UI context and the async method will resume executing on the UI thread.
Behind the scenes C# compiler actually converts your code into a state machine. It generates a lot more code so that behind the scenes every time a await task or async action is completed, it'll continue execution from where it left off. In terms of your question, every time the async action has finished, the async method will be called back on the calling thread when you originally started the call to the async method. Eg it'll execute your code on the thread that you started on. So the async action will be run on a Task thread, then the result will be returned back on the thread you method was originally called on and keep executing.
Await will get the value from the Task or async action and "unbox" it from the task when the execution is returned. In this case it will automatically put it into the int value, so no need to store the Task<int>.
Your code has the problem where it await's on the LongRunningTask() you'd most likely just want to return the long task method without the async, then have your MyMethod perform the await.
int value = await LongWaitingTask()
Async Await and the Generated StateMachine
It's a requirement of async methods that you return a Task or void.
It's possible to change it so when you return back from executing the async task it will execute the remaining code on the thread the async task was performed on using the Task.ConfigureAwait method.
It may be easier to think about it this way:
Whenever you have an await, the compiler splits your method into 2: one part before the await and another part after it.
The second part runs after the first one has finished successfully.
In your code, the first method will look like something roughly equivalent to this:
public async Task MyMethod()
{
Task<int> longRunningTask = LongRunningOperation();
MySynchronousMethod();
longRunningTask.ContinueWith(t => part2(t.Result));
}
void part2(int result)
{
Console.WriteLine(result);
}
Few important notes:
It's obviously much more complex than this since it should support
try-catch and others. Also, it doesn't really use the task
Task is not actually being used directly. It's using the task's GetAwaiter() method and its API, or any other class with this method or extension method.
If there are multiple awaits in a method it's being split multiple times.
If MyMethod is being split, how does someone who awaits MyMethod knows when all parts are done? When your async method returns a Task, the compiler generates a special Task which tracks everything with a state machine.

Categories

Resources