Async thread body loop, It just works, but how? - c#

I have just tested something that I was sure would fail miserably, but to my surprise, it worked flawlessly, and proves to myself that I am still quite mystified by how async-await works.
I created a thread, passing an async void delegate as the thread's body.
Here's an oversimplification of my code:
var thread = new Thread( async () => {
while( true ) {
await SomeLengthyTask();
...
}
});
thread.Start();
thread.Join();
The thing is that as far as I understand, when the execution hits the await keyword, there is an implicit return from the method, in this case the body of the looping thread, while the rest of the code is wrapped in a callback continuation.
Because of this fact, I was pretty sure that the thread would terminate as soon as the await yielded execution, but that's not the case!
Does anybody know how this magic is actually implemented? Is the async functionality stripped down and the async waits synchronously or is there some black magic being done by the CLR that enables it to resume a thread that has yielded because of an await?

The thread is indeed terminated very quickly.
But since the Thread constructor doesn't accept an async lambda what you got there is an async void delegate.
The original thread will end and the continuation (the rest after the await) will be posted to the ThreadPool and eventually run on another thread.
You can test that by checking the thread id:
var thread = new Thread(async () =>
{
while (true)
{
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
await SomeLengthyTask();
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
}
});
thread.Start();
thread.Join();
Console.ReadLine();
Output:
3
5
5
...
5
To make the example simpler let's assume you have this Run method:
void Run(Action action)
{
action();
}
And you call it with your async delegate
Run(async () =>
{
while(true)
{
await SomeLengthyTask();
...
}
});
The execution of Run will complete almost immediately when it reaches the first await and returns. The rest of the async delegate will continue on the ThreadPool with another thread.
Generally, each time you reach an await in the execution of an async method the thread is lost and the continuation (the rest after the awaited task completes) will be posted to the ThreadPool (unless if there's a SynchronizationContext present, like in the UI thread). It may be that it execution will be on the same thread (as in my example with 5) but it also may not.
In your case the thread you create explicitly isn't part of the ThreadPool so it will definitely be terminated and the rest will run on a different thread.

Related

How to create a thread with await methods inside?

I'm creating a thread
Thread MyThread = new Thread(async () => await MyTask());
MyThread.Start();
It kinda doesn't make sense and that's the question of how to create it properly.
MyTask is an unending task which periodically starts cpu intensive work
async Task MyTask ()
{
while (true)
{
await Task.Run(() => CPU_Load());
await Task.Delay(1000).ConfigureAwait(false);
}
}
CPU_Load is a method where work is being done
void CPU_Load ()
{
*CPU_Load*
}
The Thread constructor does not understand async delegates. You can read about this here:
Is it OK to use "async" with a ThreadStart method?
Async thread body loop, It just works, but how?
The "proper" way to execute periodically some code depends on where you want to run your code. Do you have any reason to run it on a dedicated thread? Some components are thread-affine, and require to be manipulated by the same thread for the entirety of their existence. If you have this (not very common) requirement, you can use the Thread constructor without async/await:
var myThread = new Thread(() =>
{
while (true)
{
var delayTask = Task.Delay(1000);
CPU_Load();
delayTask.Wait();
}
});
myThread.IsBackground = true;
myThread.Start();
Notice how the Task.Delay task is created before the CPU-bound operation, and then waited afterwards. This way the interval between two subsequent invocations of the CPU_Load method will be constant. It will not depend on the duration of the call itself.
If you don't need a dedicated thread, you can do the work more economically by using reusable threads from the ThreadPool. Which is exactly what your current MyTask implementation does, with the Task.Run method inside. To start the task is as easy as invoking the asynchronous method:
var myTask = MyTask();
Now the task is running, and will continue running until the process terminates, or the CPU_Load invocation fails, whatever comes first.
Another way to implement the asynchronous MyTask method would be to wrap the whole loop in a Task.Run, instead of having the Task.Run inside the loop. Functionally and performance-wise is almost identical:
var myTask = Task.Run(async () =>
{
while (true)
{
var delayTask = Task.Delay(1000);
CPU_Load();
await delayTask;
}
});
I have omitted the ConfigureAwait(false) because it's not really needed in this case. The ThreadPool does not have a synchronization context that can be captured by the await.
You could also consider using a Timer to run periodically your work, but in my opinion the Task-based approach is superior. It's quite tricky to enforce a non-overlapping execution policy with constant interval using a Timer. Caveats:
The System.Timers.Timer class is not thread-safe.
It allows overlapping invocations of the event handler.
It swallows any exceptions thrown inside the handler.
It is possible for the Elapsed event to be raised after stopping the Timer with the Stop method.
There is no easy way to stop the timer, and then wait for all running event handlers to complete.
The class is disposable and needs to be disposed.
Quoting a comment by Flydog57:
How about the original async strategy back when Main couldn't be async. Start your thread with a non-async function (or delegate or lambda). In that function that simply says WhatIWantToDoAsync().Wait(); that is async and returns a Task. Your thread function never returns, so waiting for it isn't going to do much.

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

Waiting for a background thread in C#

What's the difference between:
public class Worker
{
public static void Execute()
{
ExecuteInBackgroundThread().Wait();
}
private static Task ExecuteInBackgroundThread()
{
return Task.Run(() =>
{
// do some long running operation
});
}
}
and
public class Worker
{
public static void Execute()
{
ExecuteInBackgroundThread().Wait();
}
private static async Task ExecuteInBackgroundThread()
{
await Task.Run(() =>
{
// do some long running operation
});
}
}
I noticed that calling the second version of Worker.Execute() from the UI thread my Windows Phone app gets stuck.
Instead, using the first version everything seems to work fine.
Is the second version of ExecuteInBackgroundThread actually returning a Task<T> that can be awaited? But if that is not the case, should the compiler not give an error saying that we are not returning anything?
First of all, the real reason your app gets stuck is that you have a deadlock. That's because you're using Task.Wait which is blocking the UI thread on a task that is waiting for the UI Thread to complete.
That happens because there's only a single UI thread and so there's a SynchronizationContext that only uses that specific thread.
You can fix your deadlock by using ConfigureAwait(false), which doesn't use the SynchronizationContext:
private static async Task<T> ExecuteInBackgroundThread<T>()
{
await Task.Run(() =>
{
// do some long running operation
}).ConfigureAwait(false);
}
Other than that, both options are pretty much the same as long as there's no more code in ExecuteInBackgroundThread which is outside of the call to Task.Run. The first option is slightly faster because you're using the task from Task.Run directly. The second option adds another redundant async layer (including a state machine, etc.) on top.
Conclusion: You should use the first option. But don't use Wait which not only blocks your threads, but may result in a deadlock in some cases.
Let's see what calling Execute for each version does:
The first version calls ExecuteInBackgroundThread which immediately returns a Task. Then Execute blocks until the returned task is finished.
The second version calls ExecuteInBackgroundThread which creates a Task and schedules a continuation which will be executed when the task if finished, and returns another task. Then Execute blocks until the returned task is finished.
The difference is that the second version has a continuation scheduled because of the await keyword. The effect of it depends on the synchronization context of the thread in which you call Execute. When you are on a UI thread the second version will deadlock, because the continuation is scheduled on the UI thread which is blocked because of the call to Wait.
If you are using a console application then there won't be a deadlock because the default synchronization context uses the thread pool to execute the continuations.

Invoke failed in a task

I have this code :
var task = System.Threading.Tasks.Task.Factory.StartNew(() =>
{
// Code...
Invoke(new Action(() => progressBar.Increment(1)));
// Code...
});
task.Wait();
But the Invoke fail, without Exception or any kind of error.
It seems that the "wait" prevents the refresh of the form, probably because I'm still in the method.
how do I get around this?
Thanks
You got yourself a dead-lock.
When the Task is running (on a different thread) and you call Invoke, it wants to invoke it on the mainthread. But the mainthread is waiting for the Task to complete... (task.Wait() will block until it's ready)
What if you use:
await task;
The method using this code should be marked as async. The compiler will cut the method into pieces, and while waiting, the messageloop/thread runs again. When the task is ready, the rest of the method will be executed. (below the await task;)
Look here for more info: await vs Task.Wait - Deadlock?
This is a deadlock. The UI thread is blocking on the task.Wait() line, waiting for the task to finish. The task is blocking on Invoke because it needs to tell the UI thread to update the progress bar.
You should NOT be waiting for a task to complete on the UI thread, and should remove task.Wait(). The UI will update as soon as the task finishes.
Any code you were going to put below task.Wait() should go inside the Invoke action:
var task = System.Threading.Tasks.Task.Factory.StartNew(() =>
{
// do task work
Invoke(new Action(() => {
progressBar.Increment(1);
// other code that needs the task to be finished should go here
}));
});
// don't call task.Wait() here

Categories