Task.Run how it works [closed] - c#

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
Improve this question
I guess it's simple but I have some misunderstanding in the work of tasks
When I call the code below it's works well, I get a result in about 1 sec.
return Task.Run(() => SendRequest<IADsystem[]>(path)).Result;
But this one never returns a result:
Task<IADsystem[]> task = SendRequest<IADsystem[]>(path);
task.Wait(); //also tried without wait
return task.Result;
I thought maybe the task didn't start and I need to call Start(), but when I made it I got "Start may not be called on a promise-style task" exception.
SendRequest method:
private async Task<T> SendRequest<T>(string requestUri) where T : class
{
var authHandler = new HttpClientHandler();
authHandler.Credentials = CredentialCache.DefaultNetworkCredentials;
using(var client = new HttpClient(authHandler))
{
client.BaseAddress = apiServerURI;
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(jsonAcceptType);
HttpResponseMessage response = await client.GetAsync(requestUri);
if (response.IsSuccessStatusCode)
{
return await response.Content.ReadAsAsync<T>();
}
else
return null;
}
}
Please, explain how it works

Consider the Wait() approach first:
Task<IADsystem[]> task = SendRequest<IADsystem[]>(path);
task.Wait(); //also tried without wait
return task.Result;
This code is using the sync-over-async antipattern. This can cause a deadlock, as I describe in detail on my blog. The core problem is that your code is blocking on asynchronous code. This works in some scenarios, but in this case you're seeing the common deadlock.
In summary, this is due to await capturing the current context (usually a SynchronizationContext), and using that to resume its async method. Thus, if you call this code from a UI thread (or ASP.NET request context), then the awaits inside SendRequest will attempt to resume on that context, but the context is blocked by the Wait/Result, resulting in deadlock.
Note that the problem is due to synchronously calling an asynchronous method; the ideal solution is to go "async all the way", as I describe in my async best practices article.
Back to the actual question:
Task.Run how it works
When I call the code below it's works well
return Task.Run(() => SendRequest<IADsystem[]>(path)).Result;
Sort of. It "works" in the sense that it does not deadlock. However, it's not ideal because it blocks a thread pool thread for the duration of the request. A better solution would be to use async all the way.
But leaving that aside for the moment, the reason why it works is because it "steps outside" the calling context. Task.Run executes the SendRequest on a thread pool thread (one without a UI or ASP.NET request context), and as such the awaits in SendRequest resume on thread pool threads. This is why your calling thread can block on it without deadlocking.
But it still shouldn't block on the task; it should await it instead.

async/await methods must be async/await all the way through. If you are using Windows Forms, then whatever event handler contains the code that calls SendRequest must be marked as async. Every method between the UI event handler and your SendRequest must also be marked async, and they must be awaited. You should then
var result = await SendRequest<IADsystem[]>(path);
like Fabio suggests.
Here is some more information about async/await.

I started learning about async here in this page so I assume it is good starting point. I urge you to look at execution context Async - Starting Point
Coming to why you have a dead lock, I will try to explain. When you called task.Wait(), you are basically telling your current thread to wait for the result to continue. So right now your current thread, or your Execution Context is waiting for the result to continue. Let us continue. When you called task.Wait(), you entered private async Task<T> SendRequest<T>(string requestUri)... method and the moment you reached return await response.Content.ReadAsAsync<T>(); you passed your current execution context, which is waiting the result from SendRequest<T>. Right now, you are in a dead lock. Why? Your UI thread is waiting an answer from SendRequest<T> and in order for SendRequest<T> to finish (or return), it needs to access to UI thread but you can not access the UI thread because it is waiting for SendRequest<T> to finish but for SendRequest<T> to finish it needs to access to UI thread....

Related

How does control flow when there is synchronous code in awaited function?

Say in button click event I have:
await someFn()
And in the someFn code, I have got some synchronous code and some asynchronous code.
As per online reading on this topic, they all seem to say that when await is encountered, control immediately goes back the caller (UI thread in this case) while the asynchronous waiting happens.
I haven't come across situation where it is explained what happens when there is some synchronous code just before the async code in the awaited function. Does the synchronous code execute before passing the control to the caller (UI thread)?
I know that- had it been run using await Task.Run(...) instead of await someFn() then it would execute in a separate thread.
It is true that "when await is encountered, control immediately goes back the caller", under the condition that the awaitable will not be completed at the await point. Otherwise (if the awaitable is already completed) the control will stay inside the method, by executing immediately the code that follows the await.
That's all that influences the behavior of the await operator: the result of the IsCompleted property of the awaiter. It doesn't matter if the method that produced the awaitable included synchronous code or not. The await doesn't know, and doesn't care. A synchronous code block inside the someFn will indeed make a difference regarding the responsiveness of your application, but that has nothing to do with the await. To understand why, it may help to rewrite your code in a more verbose but equivalent form. This:
await someFn();
...is equivalent to this:
Task t = someFn();
await t;
The UI of your application will freeze because the someFn() blocks, not because the await t blocks. Unless of course you have created some wicked task-like awaitable type, that blocks when the IsCompleted property of its awaiter is invoked.
If you see an await in your code, for example when you see var obj = await someFn();, then you can treat is as a placeholder for the following code:
var waitingTask task = someFn();
if (!task.IsCompleted) {
var thisTask = [Magic]
return thisTask.Task;
}
var obj = waitingTask.Result;
In reality the code is rewritten to something more complex, a so called state machine. The [Magic] part is hard to explain but what it does is capturing the current SynchronizationContext, the current line that is executing and the task it's waiting on.
As soon as the task that you are waiting on completes, it will try to restore this method on the captured SynchronizationContext and continue working. It's this SynchronizationContext that tells you if you are working on the UI thread or not. This is why it's advised to use ConfigureAwait(false) to avoid restoring on the UI thread if it's not needed, avoiding possible slow downs on the UI for executing code that does nothing with the UI.
The main point here is that you see that the code before an await is executed synchronously/immediately. Even the code inside the function that you are calling is executed synchronously/immediately until you reach an await in that function that is not completed. From this point on the stack 'rewinds', creating (returning) a new Task on each point that waits for the underlying code to complete. If the lowest task completes, only that task that waits for it is 'revived', all tasks above it still stay 'waiting' and only when that bottom function has reached it real end of the method will it 'revive' the task that is waiting for it's result, and so on to the top of the stack.

Use ConfigureAwait(false) or Task.Run to avoid blocking UI Thread

I'm trying to find out which approach is better let's say we have a button, after user clicks it we perform 1. Send a async request using httpClient
2. Some heavy synchronous staff like computations and saving data to a database.
Like that:
button1.Click += async(sender, e) =>
{
bool a = await Task.Run(async () => { return await MyTask1();});
}
async Task<bool> MyTask1()
{
await new HttpClient().GetAsync("https://www.webpage.com");
DoHeavyStuffFor5minutes();
return true;
}
button2.Click += async(sender, e) =>
{
bool a = await MyTask2();
}
async Task<bool> MyTask2()
{
await new HttpClient().GetAsync("https://www.webpage.com").ConfigureAwait(false);
DoHeavyStuffFor5minutes();
}
From what i understand GetAsync does not block my UI thread because underneath it uses a method which make it runs on different thread perhaps Task.Run or any other method that allows that.
But DoHeavyStuffFor5Minutes will block my UI because it will get called on the caller SynchornizationContext.
So i read that using ConfigureAwait(false) will make code after GetAsync do not run on the same SynchornizationContext as the caller. My question is, which approach is better first or the second one?
There is no need to execute HttpClient.GetAsync on a background thread using Task.Run since the HTTP request is truly asynchronous by nature so in this case your second approach is better that the first one.
When the Task returned by GetAsync has eventually finished, the remainder or MyTask2() will be executed on a thread pool thread assuming you opt out of capturing the context by calling ConfigureAwait(false).
Note however that ConfigureAwait(false) does not guarantee that the callback or remainer won't be run in the original context in all cases.
From Stephen Toub's blog post:
Does ConfigureAwait(false) guarantee the callback won’t be run in the original context?
"No. It guarantees it won’t be queued back to the original contex...but that doesn’t mean the code after an await task.ConfigureAwait(false) won’t still run in the original context. That’s because awaits on already-completed awaitables just keep running past the await synchronously rather than forcing anything to be queued back. So, if you await a task that’s already completed by the time it’s awaited, regardless of whether you used ConfigureAwait(false), the code immediately after this will continue to execute on the current thread in whatever context is still current."
So you might want to off-load DoHeavysTuffFor5minutes, which I assume is a CPU-bound and potentially long-running operation, to a background thread using Task.Run to be on the safe side. At least in the general case.
Also note that a method that is named *Async and returns a Task or Task<T> might still block the calling thread depending on its implementation. In general, this may be a reason to use your first approach of a calling both methods on a background thread in order to avoid blocking the UI thread. If you however use well-implemented APIs, such as HttpClient, this isn't an issue though.

Background started task does not finish/gets terminated after the first encountered await

In an ASP.NET application, I have an action which when hit, starts a new background task in the following way:
Task.Factory.StartNew(async () => await somethingWithCpuAndIo(input), CancellationToken.None, TaskCreationOptions.DenyChildAttach | TaskCreationOptions.LongRunning, TaskScheduler.FromCurrentSynchronizationContext());
I'm not awaiting it, I just want it to start, and continuing doing its work in the background.
Immediately after that, I return a response to the client.
For some reason though, after the initial thread executing the background work hits an await that awaits the completion of a method, when debugging, I successfully resolve the method, but upon returning, execution just stops and does not continue below that point.
Interestingly, if I fully await this task (using double await), everything works as expected.
Is this due to the SynchronizationContext? The moment I return a response, the synchronizationContext is disposed/removed? (The SynchronizationContext is being used inside the method)
If it is due to that, where exactly does the issue happen?
A) When the Scheduler attempts to assign the work on the given synchronizationContext, it will already be disposed, so nothing will be provided
B) Somewhere down the lines in the method executing, when I return a response to the client, the synchronizationContext is lost, regardless of anything else.
C) Something else entirely?
If it's A), I should be able to fix this by simply doing Thread.Sleep() between scheduling the work and returning a response. (Tried that, it didn't work.)
If it's B) I have no idea how I can resolve this. Help will be appreciated.
As Gabriel Luci has pointed out, it is due the the first awaited incomplete Task returning immediately, but there's a wider point to be made about Task.Factory.StartNew.
Task.Factory.StartNew should not be used with async code, and neither should TaskCreationOptions.LongRunning. TaskCreationOptions.LongRunning should be used for scheduling long running CPU-bound work. With an async method, it may be logically long running, but Task.Factory.StartNew is about starting synchronous work, the synchronous part of an async method is the bit before the first await, this is usually very short.
Here is the guidance from David Fowler (Partner Software Architect at Microsoft on the ASP.NET team) on the matter:
https://github.com/davidfowl/AspNetCoreDiagnosticScenarios/blob/86b502e88c752e42f68229afb9f1ac58b9d1fef7/AsyncGuidance.md#avoid-using-taskrun-for-long-running-work-that-blocks-the-thread
See the 3rd bulb:
Don't use TaskCreationOptions.LongRunning with async code as this will
create a new thread which will be destroyed after first await.
Your comments made you intentions a little clearer. What I think you want to do is:
Start the task and don't wait for it. Return a response to the client before the background task completes.
Make sure that the somethingWithCpuAndIo method has access to the request context.
But,
A different thread won't be in the same context, and
As soon as the first await is hit, a Task is returned, which also means that Task.Factory.StartNew returns and execution of the calling method continues. That means that the response is returned to the client. When the request completes, the context is disposed.
So you can't really do both things you want. There are a couple of ways to work around this:
First, you might be able to not start it on a different thread at all. This depends on when somethingWithCpuAndIo needs access to the context. If it only needs the context before the first await, then you can do something like this:
public IActionResult MyAction(input) {
somethingWithCpuAndIo(input); //no await
}
private async Task somethingWithCpuAndIo(SomeThing input) {
// You can read from the request context here
await SomeIoRequest().ConfigureAwait(false);
// Everything after here will run on a ThreadPool thread with no access
// to the request context.
}
Every asynchronous method starts running synchronously. The magic happens when await is given an incomplete Task. So in this example, somethingWithCpuAndIo will start executing on the same thread, in the request context. When it hits the await, a Task is returned to MyAction, but it is not awaited, so MyAction completes executing and a response gets sent to the client before SomeIoRequest() has completed. But ConfigureAwait(false) tells it that we don't need to resume execution in the same context, so somethingWithCpuAndIo resume execution on a ThreadPool thread.
But that will only help you if you don't need the context after the first await in somethingWithCpuAndIo.
Your best option is to still execute on a different thread, but pass the values you need from the context into somethingWithCpuAndIo.
But also, use Task.Run instead of Task.Factory.StartNew for reasons described in detail here.
Update: This can very likely cause unpredictable results, but you can also try passing a reference to HttpContext.Current to the thread and setting HttpContext.Current in the new thread, like this:
var ctx = HttpContext.Current;
Task.Run(async () => {
HttpContext.Current = ctx;
await SomeIoRequest();
});
However, it all depends on how you are using the context. HttpContext itself doesn't implement IDiposable, so it, itself, can't be disposed. And the garbage collector won't get rid of it as long as you're holding a reference to it. But the context isn't designed to live longer than the request. So after the response is returned to the client, there may be many parts of the context that are disposed or otherwise unavailable. Test it out an see what explodes. But even if nothing explodes right now, you (or someone else) might come back to that code later, try to use something else in the context and get really confused when it blows up. It could make for some difficult-to-debug scenarios.

Application locked by "await"?

I am using async method as extension for StorageFile to return file's size. Problem is, this method deadlocks somehow my Windows Store app. Issue is within awaiting result from file.GetBasicPropertiesAsync. I am not very experienced in asynchronous programming, but I supposed await statement is there to prevent such situations. Does anyone see something obvious what I don't get?
Deadlock do not occur in debug, if I wait for some time with GetBasicPropertiesAsync.
Method:
public static async Task<string> GetSizeMB(this StorageFile file)
{
var properties = await file.GetBasicPropertiesAsync();
double sizeDouble = Convert.ToDouble(properties.Size) /1000000;
return $"{Math.Round(sizeDouble, 2)} MB";
}
Thanks in advance for hints.
If you look further up your call stack, you're almost certainly calling a task's Result or Wait member from the UI thread. This is not permitted on Windows Store applications, since it can freeze the UI thread.
I describe this kind of deadlock in detail on my blog, but the summary version is as follows:
When await has to (asynchronously) wait, by default it will first capture a "context". This "context" is SynchronizationContext.Current unless it is null, in which case it is TaskScheduler.Current. Later, when the (asynchronous) wait is complete, the remainder of your async method continues executing on that captured context.
In this case, the context is the UI SynchronizationContext. Further up your call stack, another method calls GetSizeMB and gets back a task, and then (presumably) calls Result or Wait on that task. This is the actual cause of the deadlock, because that code is blocking the UI thread until GetSizeMB completes. But GetSizeMB cannot complete because it needs to resume executing on the UI thread.
The proper fix is to replace Result/Wait with await.

What happens while waiting on a Task's Result?

I'm using the HttpClient to post data to a remote service in a .NET 4.0 project. I'm not concerned with this operation blocking, so I figured I could skip ContinueWith or async/await and use Result.
While debugging, I ran into an issue where the remote server wasn't responsive. As I stepped through the code, it seemed like my code just stopped running on the third line... the current stack pointer line stopped being highlighted yellow, and didn't advance to the next line. It just disappeared. It took me a while to realize that I should wait for the request to timeout.
var client = new HttpClient();
var task = client.PostAsync("http://someservice/", someContent);
var response = task.Result;
My understanding was that calling Result on the Task caused the code to execute synchronously, to behave more like this (I know there is no Post method in the HttpClient):
var client = new HttpClient();
var response = client.Post("http://someservice/", someContent);
I'm not sure this is a bad thing, I'm just trying to get my head around it. Is it really true that by virtue of the fact that the HttpClient is returning Tasks instead of the results directly, my application is automatically taking advantage of asynchrony even when I think I'm avoiding it?
In Windows, all I/O is asynchronous. Synchronous APIs are just a convenient abstraction.
So, when you use HttpWebRequest.GetResponse, what actually happens is the I/O is started (asynchronously), and the calling thread (synchronously) blocks, waiting for it to complete.
Similarly, when you use HttpClient.PostAsync(..).Result, the I/O is started (asynchronously), and the calling thread (synchronously) blocks, waiting for it to complete.
I usually recommend people use await rather than Task.Result or Task.Wait for the following reasons:
If you block on a Task that is the result of an async method, you can easily get into a deadlock situation.
Task.Result and Task.Wait wrap any exceptions in an AggregateException (because those APIs are holdovers from the TPL). So error handling is more complex.
However, if you're aware of these limitations, there are some situations where blocking on a Task can be useful (e.g., in a Console application's Main).
Capturing the result of a task blocks the current thread. There is no point in using a async version of a method in this case. Post() and PostAsync().Result will both block.
If you want to make use of concurrency, you should write it as such:
async Task PostContent()
{
var client = new HttpClient();
Task t = await client.PostAsync("http://someservice/", someContent);
//code after this line will execute when the PostAsync completes.
return t;
}
Since PostContent() itself returns a Task, the method calling it should also await.
async void ProcessResult()
{
var result = await PostContent();
//Do work with the result when the result is ready
}
For instance, if you call ProcessResult() in a button click handler, you see that the UI is still responsive, other controls still function.

Categories