`await Task.Run(() => DoWork1Async())` instead of `await DoWork1Async()`? - c#

There are following methods,
async Task DoWork1Async() { .... };
async Task DoWork2Async() { .... };
async Task DoWork3Async() { .... };
I read the following code
await Task.Run(() => DoWork1Async());
await Task.Run(() => DoWork2Async());
await Task.Run(() => DoWork3Async());
instead of
await DoWork1Async();
await DoWork2Async();
await DoWork3Async();
What's the difference between these two?

When you run (within a method marked async)
await DoWork1Async();
your code calls DoWork1Async within a state machine the compiler sets up. At that point, you code relinquishes control back to that state machine. When the task completes, the rest of the code in your method continues.
Remember that async code doesn't necessarily run on a separate thread (for example, if you are doing asynchronous I/O).
When you run:
await Task.Run(() => DoWork1Async());
your DoWork1Async is dispatched as work to the Thread Pool. It is executed on a different thread (a thread pool thread). When that work is completed, that same state machine mechanism hands control back to you code to continue running.
In the second case, your code always runs on a thread pool thread. In the first case, you may not be using an extra thread at all (depending on how DoWork1Async is coded)

await Task.Run(() => DoWork1Async());
Starts a Task, which will call DoWork1Async. This is guaranteed to be asynchronous no matter how DoWork1Async is implemented.
Note: Task.Run(Action<Task<T>>) returns a Task<T>,
not a Task<Task<T>>. When your lambda returns a Task, Task.Run() returns a Task that completes with the result of the inner task. Thus you do not need to do awkward things like await await Task.Run(() => return a Task).
await DoWork1Async();
Calls DoWork1Async synchronously on the current thread. If DoWork1Async executes an await statement, then work will be suspended and the remainder of the work will occur asynchronously. However, if DoWork1Async completes execution without hitting an await, then control will return to your caller and will then start DoWork2Async all synchronously.
So in short, the first form guarantees the DoWork1Async won't start or finish synchronously.
The second form will start DoWork1Async synchronously, and may even finish it synchronously depending upon how it is written.
--
Here's a fiddle showing the difference:
https://dotnetfiddle.net/GhrO8x
Notice how in the first case DoWork() starts and executes completely synchronously before we even await its Task while in the 2nd case it executes asynchronously after we await its task.

Related

Can somebody explain this deadlock behaviour

TLDR: In my example (ASP.NET) below why does task1.Result and task2.Result result in a deadlock and task3.Result does not?
We have a class in our code, where I cannot easily mark the method as async and keep the code from blocking while waiting for an async task to finish. The reason is that our framework does not support that. Therefore I tried to find a solution with task.Result and got some deadlocks. Luckily I found a solution (see task3). Now I tried to find out, what is the difference between task1, task2 and task3 to understand why the first two result in a deadlock and the third does not.
Using the debugger I did not see any difference like task3 being run before task3.Result is called. It is still in WaitingForActivation state like the other two. Can anyone explain to me, how task3 can work?
public class HomeController : Controller
{
public ActionResult GetSomething()
{
var task1 = GetSomethingAsync();
var task2 = Task.Run(async () => await task1);
var task3 = Task.Run(async () => await GetSomethingAsync());
return Content(task1.Result);
// return Content(task2.Result);
// return Content(task3.Result);
}
private static async Task<string> GetSomethingAsync()
{
return await Task.Run(() => "something");
}
}
The root of the problem is the await here:
private static async Task<string> GetSomethingAsync()
{
return await Task.Run(() => "something");
}
The context is captured before entering the awaiting, and the context is the main thread. So after the awaiting the context must be restored, so the continuation is scheduled to run in the main thread. Since the main thread is blocked at this point, it can't process the continuation, hence the deadlock.
To prevent the capturing of the context you could configure this await with ConfigureAwait(false).
Update: By continuation I mean the code inside GetSomethingAsync that follows after the awaiting. Although there is no code after that, it seems that the compiler does bother to create an actual continuation for this no-op part of the method (otherwise a deadlock should not occur in your example).
It should be noted that the compiler transforms an async method to a Task that consists of multiple mini-tasks. Every await encountered in the path of the execution causes the creation of a mini-task that is the continuation of the awaited task. All these mini-tasks are completed one after the other, and the completion of the last one signals the completion of the "master-task" that is returned by the async method.
What ever GetSomethingAsync() does it is done by the calling thread until some operation (OP) can not be completed right away (for example io) then the controlflow is given to the calling function but.
If you then access the Result property on the returned Task object the thread will be blocked.
This leads to the problem that even is the OP is finished the thread will not know about it because he is busy waiting for the completion of the Task
If however you let GetSomethingAsync() be exectuted by some thread-pool thread (which Task.Run(...) does) the thread-pool thread can finish the OP and the calling thread can be notified that the Task has completed.
Update:
Your second approch does not work because the task was still started on your main thread. If you have this methode
public static async Task DoStuffAsync()
{
Console.WriteLine($"Doing some stuff1 on thread {Thread.CurrentThread.ManagedThreadId}");
await Task.Delay(50);
Console.WriteLine($"Doing some stuff2 on thread {Thread.CurrentThread.ManagedThreadId}");
}
and run this code in an appilcation with an SynchronizationContext
var task = DoStuffAsync();
Console.WriteLine($"Doing main stuff on thread {Thread.CurrentThread.ManagedThreadId}");
await Task.Run(async () => await task);
It will output something like:
Doing some stuff1 on thread 1
Doing main stuff on thread 1
Doing some stuff2 on thread 1
So with the code line Task.Run(async () => await task) you only achieved that a thread-pool thread waits on the completion of your original Task, but this in turn creates a new Task that if not handeld by awaiting it causes a deadlock.

Deadlock with async Task.Run method with Wait from synchronous method and timeout

I have a method defined as:
public Task<ReturnsMessage> Function()
{
var task = Task.Run(() =>
{
var result = SyncMethod();
return new ReturnMessage(result);
});
if (task.Wait(delay))
{
return task;
}
var tcs = new TaskCompletionSource<ReturnMessage>();
tcs.SetCanceled();
return tcs.Task;
}
Now it is called in a loop based on maxAttempts value:
(method name RetryableInvoke)
for (var i = 0; i < maxAttempts; i++)
{
try
{
return Function().Result;
}
catch (Exception e)
{
}
}
It works perfectly fine, however when there is a massive load I am finding that the threads are increasing drastically and dump is showing me this warnings:
Can anyone suggest me the best possible way to handle this situation, so that I don't see any kind of deadlocks?
You are not deadlocking the application. While Task.Run would execute the delegate on a background thread, the following call of Task.Wait effectively converts the originally concurrent Task.Run code back to synchronous code: Task.Wait and Task.Result synchronously wait for the Task to complete.
What you assume to be a deadlock is rather the result of a frozen main thread, caused by synchronously waiting for a long-running operation to complete.
Below the provided solutions is a section that briefly explains how Task.Result, Task.Wait and Task.GetAwaiter().GetResult() can create a real deadlock.
Although the Task related deadlock and your frozen main thread have different causes, the solution to both is similar: convert the synchronous code to an asynchronous version by introducing async/await. This will keep the main thread responsive.
Using async/await will replace the synchronous Task.Wait and also make Task.Result redundant.
The following two examples show how to convert your synchronous version to asynchronous code and provide two solutions to implement a timeout for a Task: the first and not recommended solution uses Task.WhenAny together with cancellation.
The second and recommended solution uses the timeout feature of the CancellationTokenSource.
It is generally recommended to check the class API for asynchronous versions instead of using Task.Run. In case your 3rd party library exposes an asynchronous API you should use this to replace the Task.Run. This is because true asynchronous implementations access the kernel to use the system's hardware to execute code asynchronously without the need to create background threads.
Timeout implementation using Task.WhenAny (not recommended)
This example uses the characteristic of Task.WhenAny which awaits a set of Task objects and returns the one that completes first and we usually cancel the remaining ones.
When creating a timed Task for example by using Task.Delay and pass it along with other Task objects to Task.WhenAny, we can create a race: if the timed task completes first, we can cancel the remaining Task objects.
public async Task<ReturnMessage> FunctionAsync()
{
using var cancellationTokenSource = new CancellationTokenSource();
{
// Create the Task with cancellation support
var task = Task.Run(
() =>
{
// Check if the task needs to be cancelled
// because the timeout task ran to completion first
cancellationTokenSource.Token.ThrowIfCancellationRequested();
// It is recommended to pass a CancellationToken to the
// SyncMethod() too to allow more fine grained cancellation
var result = SyncMethod(cancellationTokenSource.Token);
return new ReturnMessage(result);
}, cancellationTokenSource.Token);
var timeout = TimeSpan.FromMilliseconds(500);
// Create a timeout Task with cancellation support
var timeoutTask = Task.Delay(timeout, cancellationTokenSource.Token);
Task firstCompletedTask = await Task.WhenAny(task, timeoutTask);
// Cancel the remaining Task that has lost the race.
cancellationTokenSource.Cancel();
if (firstCompletedTask == timeoutTask)
{
// The 'timoutTask' has won the race and has completed before the delay.
// Return an empty result.
// Because the cancellation was triggered inside this class,
// we can avoid to re-throw the OperationCanceledException
// and return an error/empty result.
return new ReturnMessage(null);
}
// The 'task' has won the race, therefore
// return its result
return await task;
}
}
Timeout implementation using the CancellationTokenSouce constructor overload (recommended)
This example uses a specific constructor overload that accepts a TimeSpan to configure a timeout. When the defined timeout has expired the CancellationTokeSource will automatically cancel itself.
public async Task<ReturnsMessage> FunctionAsync()
{
var timeout = TimeSpan.FromMilliseconds(500);
using (var timeoutCancellationTokenSource = new CancellationTokenSource(timeout))
{
try
{
return await Task.Run(
() =>
{
// Check if the timeout has elapsed
timeoutCancellationTokenSource.Token.ThrowIfCancellationRequested();
// Allow every called method to invoke the cancellation
var result = SyncMethod(timeoutCancellationTokenSource.Token);
return new ReturnMessage(result);
}, timeoutCancellationTokenSource.Token);
}
catch (OperationCanceledException)
{
// Return an empty result.
// Because the cancellation was triggered inside this class,
// we can avoid to re-throw the OperationCanceledException
// and return an error/empty result.
return new ReturnMessage(null);
}
}
}
To complete both of the above examples that converted the originally synchronous code (Function() to asynchronous code (FunctionAsync()), we have to await the new method properly:
// The caller of this new asynchronous version must be await this method too.
// `await` must be used up the call tree whenever a method defined as `async`.
public async Task<ReturnMessage> void RetryableInvokeAsync()
{
ReturnMessage message = null;
for (var i = 0; i < maxAttempts; i++)
{
message = await FunctionAsync();
// Never use exceptions to control the flow.
// Control the for-loop using a condition based on the result.
if (!string.IsNullOrWhiteSpace(message.Text))
{
break;
}
}
return message;
}
Why Task.Result, Task.Wait and Task.GetAwaiter().GetResult() create a deadlock when used with async code
First, because a method is defined using the async key word it supports asynchronous execution of operations with the help of the await operator. Common types that support await are Task, Task<TResult>, ValueTask, ValueTask<TResult> or any object that matches the criteria of an awaitable expression.
Inside the async method the await captures the SynchronizationContext of the calling thread, the thread it is executed on. await will also create a callback for the code that follows the await statement, called "continuation".
This callback is enqueued on the captured SynchronizationContext (by using SynchronizationContext.Post) and executed once the awaitable (e.g., Task) has signalled its completion.
Now that the callback is enqueued (stored) for later execution, await allows the current thread to continue to do work asynchronously while executing the awaitable.
async/await basically instructs the compiler to create a state machine.
Given is the following example that produces three potential deadlocks:
public void RetryableInvoke()
{
// Potential deadlock #1.
// Result forces the asynchronous method to be executed synchronously.
string textMessage = FunctionAsync().Result;
// Potential deadlock #2.
// Wait forces the asynchronous method to be executed synchronously.
// The caller is literally waiting for the Task to return.
textMessage = FunctionAsync().Wait();
// Potential deadlock #3.
// Task.GetAwaiter().GetResult() forces the asynchronous method to be executed synchronously.
// The caller is literally waiting for the Task to return.
textMessage = FunctionAsync().GetAwaiter().GetResult();
}
private async Task<string> FunctionAsync()
{
// Capture the SynchronizationContext of the caller's thread by awaiting the Task.
// Because the calling thread synchronously waits, the callers context is not available to process the continuation callback.
// This means that the awaited Task can't complete and return to the captured context ==> deadlock
string someText = await Task.Run(
() =>
{
/* Background thread context */
return "A message";
});
/*
Code after the await is the continuation context
and is executed on the captured SynchronizationContext of the
thread that called FunctionAsync.
In case ConfigureAwait is explicitly set to false,
the continuation is the same as the context of the background thread.
*/
// The following lines can only be executed
// after the awaited Task has successfully returned (to the captured SynchronizationContext)
someText += " More text";
return someText;
}
In the above example Task.Run executes the delegate on a new ThreadPool thread. Because of the await the caller thread can continue to execute other operations instead of just waiting.
Once the Task has signaled its completed, the pending enqueued continuation callback is ready for execution on the captured SynchronizationContext.
This requires the caller thread to stop its current work to finish the remaining code that comes after the await statement by executing the continuation callback.
The above example breaks the asynchronous concept by using Task.Wait, Task.Result and Task.GetAwaiter().GetResult(). This Task members will synchronously wait and therefore effectively cause the Task to execute synchronously, which means:
a) The caller thread will block itself (wait) until the Task is completed. Because the thread is now synchronously waiting it is not able to do anything else. If instead the invocation had been asynchronously using async/await, then instead of waiting, the parent thread will continue to execute other operations (e.g., UI related operations in case of a UI thread) until the Task signals its completion.
Because of the synchronous waiting the caller thread can't execute the pending continuation callback.
b) The Task signals completion but can't return until the continuation callback has executed. The remaining code (the code after Task.Wait) is supposed to be executed on captured SynchronizationContext, which is the caller thread.
Since the caller thread is still synchronously waiting for the Task to return, it can't execute the pending continuation callback.
Now the Task must wait for the caller thread to be ready/responsive (finished with the synchronous waiting) to execute the continuation.
a) and b) describe the mutual exclusive situation that finally locks both the caller and the thread pool thread: the caller thread is waiting for the Task, and the Task is waiting for the caller thread. Both wait for each other indefinitely. This is the deadlock. If the caller thread is the main thread then the complete application is deadlocked and hangs.
Because the example used Task.Wait in one place and Task.Result in another, it has created two potential deadlock situations:
From Microsoft Docs:
Accessing the property's [Task.Result] get accessor blocks the calling thread until the asynchronous operation is complete; it is equivalent to calling the Wait method.
Task.GetAwaiter().GetResult() creates the third potential deadlock.
How to fix the deadlock
To fix the deadlock we can use async/await (recommend) or ConfigreAwait(false).
ConfigreAwait(true) is the implicit default: the continuation callback is always executed on the captured SynchronizationConext.
ConfigreAwait(false) instructs await (the state machine) to execute the continuation callback on the current thread pool thread of the awaited Task. It configures await to ignore the captured SynchronizationContext.
ConfigreAwait(false) basically wraps the original Task to create a replacement Task that causes await to not enqueue the continuation callback on the captured SyncronizationContext.
It's recommended to always use ConfigreAwait(false) where executing the callback on the caller thread is not required (this is the case for library code or non UI code in general). This is because ConfigreAwait(false) improves the performance of your code as it avoids the overhead introduced by registering the callback.
ConfigreAwait(false) should be used on the complete call tree (on every await not only the first).
Note, there are exceptions where the callback still executes on the caller's SynchronizationContext, for example when awaiting an already completed Task.
public async Task RetryableInvokeAsync()
{
// Awaiting the Task always guarantees the caller thread to remain responsive.
// It can temporarily leave the context and will therefore not block it.
string textMessage = await FunctionAsync();
// **Only** if the awaited Task was configured
// to not execute the continuation on the caller's SnchronizationContext
// by using ConfigureAwait(false), the caller can use
// Wait(), GetAwaiter().GetResult() or access the Result property
// without creating a deadlock
string textMessage = FunctionAsync.Result;
}
private async Task<string> FunctionAsync()
{
// Because the awaited Task is configured to not use the caller's SynchronizationContext by using ConfigureAwait(false),
// the Task don't need to return to the captured context
// ==> no deadlock potential
string someText = await Task.Run(
() =>
{
/* Background thread context */
return "A message";
}).ConfigureAwait(false);
/*
Code after the await is the continuation context
and is not executed on the captured SynchronizationContext.
Because ConfigureAwait is explicitly set to false,
the continuation is the same as the context of the background thread.
Additionally, ConfigureAwait(false) has improved the performance
of the async code.
*/
// The following lines will always execute
// after the awaited Task has ran to completion
someText += " More text";
return someText;
}
You are starting tasks with Task.Run, then you are returning cancelled if they timeout, but you never stop the tasks. They just continue to run in the background.
Your code should be async/await and use CancellationSource and handle the cancellation token inside the SyncMethod(). But if you can't and you want, as I understand it, to run a method asynchronously and kill it forcefully after a while, you should probably use Threads and Abort them.
Warning: aborting threads is not safe unless you know what you are doing and it might even be removed from .NET in future versions.
I've actually researched this a while ago: https://siderite.dev/blog/how-to-timeout-task-and-make-sure-it.html

Understanding the difference between the following async-await calls

Would these calls behave similarly? As in would they behave the same way async-await runs - can or cannot run on the same original thread context? Or would the first one ensure that the execution happens in a different thread context?
First way -
Task task = SomeAsyncTask();
await task();
Second way -
await SomeAsyncTask();
If there is code between Task task = SomeAsyncTask(); and await task();, your task will start executing async code and your current thread will execute that code at the same time.
Otherwise, they are equal..
Task task = SomeAsyncTask();
//Codes executed on current thread while SomeAsyncTask is running
await task();
All asynchronous methods start synchronously.
That is:
await SomeAsyncTask();
is exactly the same as:
var task = SomeAsyncTask();
await task;
In both cases, SomeAsyncTask will run until it is either complete or hits an asynchronous await, at which point it returns a task. That task is then awaited by the calling method.

Async method await keyword doesn't suspend to calling method

I have following line of code, to understand async-await and suspension till awaited task complete.
public async Task<string> WaitAsynchronouslyAsync()
{
var results = await WaitAsync();
Console.WriteLine($#"I am not waiting");
return results;
}
public async Task<string> WaitAsync()
{
await Task.Delay(10000).ConfigureAwait(false);
return "Async programming suspension point demo.";
}
The await operator suspends execution until the work of the WaitAsync() (Delay 10 seconds) method is complete.
In the meantime, I was expecting that Console.WriteLine($#"I am not waiting"); should write text to output window until awaited task completes, as control is returned to the caller of WaitAsync(). When the task finishes execution, the await expression evaluates string ("Async programming suspension point demo.").
Am I confused here with suspension pointer vs debug pointer?
The await operator suspends execution until the work of the WaitAsync() method is complete.
Correct.
I was expecting that Console.WriteLine($#"I am not waiting"); should write text to output window until awaited task completes, as control is returned to the caller of WaitAsync().
Why? You just said the await operator suspends execution until the work is complete. The work will not be complete for another ten seconds; the work is to wait for ten seconds. The work of WaitAsync is not complete until it executes the return, and that is in the future.
The right thing to do here is for WaitAsynchronouslyAsync to allow its caller an opportunity to run in this scenario.
You have correctly stated the meaning of await, but you seem to not be fully internalizing the consequences. An await checks its operand to see if the task is complete. If it is, then the result of the task is fetched and the method continues. If it is not, then the remainder of the method is signed up as the continuation of that task, and control is yielded so that more work can be done elsewhere. In the future, when the task completes, the remainder of the method is scheduled to execute.
await means "wait until this finishes executing asynchronously" your function will not continue until the supplied Task finishes executing.
If you instead said the following it wouldn't wait:
var results = WaitAsync();
Console.WriteLine($#"I am not waiting");
return await results;
Or even more succinctly, remove the async from the signature and then you can just return results;.
The sequence of operations will go something like this:
You call WaitAsynchronouslyAsync
WaitAsynchronouslyAsync calls WaitAsync
WaitAsync calls Task.Delay(10000).ConfigureAwait(false), which produces a Task.
WaitAsync returns a new Task to its caller, WaitAsynchronouslyAsync, which will be completed later
WaitAsynchronouslyAsync awaits the task from WaitAsync, and then returns to it's caller, providing a Task representing when it will be finished.
Other stuff happens further up the call stack.
Task.Delay finishes, so the continuation of WaitAsync fires, picking up where it left off.
WaitAsync returns the string, completing the Task.
WaitAsynchronouslyAsync can now continue, since the Task returned from WaitAsync has finished. It will go on to print out text to the console.
WaitAsynchronouslyAsync finishes it's task, letting anyone who has anything set up to run when it's finished to be able to run.

Blocking and async in wcf

This example partly is taken from stack
public async Task MyMethod(string s1,sring s2)
{
Trace.TraceInformation("info");
//check input
if (s1 == null || s2 == null)
Trace.TraceInformation("error");
Task<int> longRunningTask = LongRunningOperation();
//indeed you can do independent to the int result work here
//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(1000); //1 seconds delay
return 1;
}
I want the MyMethod to be not blocking.
1.so is it correct that until Task<int> longRunningTask = LongRunningOperation();
the user is blocked?Should I runn the above part of code (the trace and input check) asynchronously too?if yes how? is there a way to do that without new async method implementation?
2.When we start:
Task<int> longRunningTask = LongRunningOperation();
starts executing LongRunningOperation
independent work is done on let's assume the Main Thread (Thread ID = 1) then await longRunningOperation is reached.
Now, 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).
3.What happens if exception is thrown during async method?
4.If the synchronous implementation is provided can I use it?how?
1. You are incorrect about where the code retuns to the user.
Everything up to int result = await longRunningTask; is run on the same thread as the function that called MyMethod(, not the Task<int> longRunningTask = LongRunningOperation(); call.
If you want the synchronous portion of the function to also run in a background thread wrap the call to MyMethod in a Task.Run(
var myMethodTask = Task.Run(() => MyMethod("foo", "bar"));
you then can await or whatever you need on that returned task.
2. If longRunningTask is complete by the time you reach await longRunningTask the function will still continue to run on the same thread and never return to the caller till it reaches the end of the function. When it reaches the end of the function it returns to the caller with a Task already in the completed state.
If longRunningTask was not done it will return to the caller at the point of the await and return a Task that will change from the Running state to the Completed state when the function completes. When the await returns it uses the SynchronizationContext that was active when the task started to figure out what thread it should continue it's work on. If you where on the UI thread when you hit the await the work will continue on the UI thread, by default it will use a ThreadPool thread to continue the work if there was no SynchronizationContext set.
3. If a exception is thrown you see the exception when await longRunningTask returns (or the parent thread returns if you wrapped the function in a Task.Run()
4. You should use a separate method for the synchronous implementation. See the two Microsoft blog posts "Should I expose asynchronous wrappers for synchronous methods?" and "Should I expose synchronous wrappers for asynchronous methods?"

Categories