Await VS Task.Wait - Deadlock

await vs Task.Wait - Deadlock?

Wait and await - while similar conceptually - are actually completely different.

Wait will synchronously block until the task completes. So the current thread is literally blocked waiting for the task to complete. As a general rule, you should use "async all the way down"; that is, don't block on async code. On my blog, I go into the details of how blocking in asynchronous code causes deadlock.

await will asynchronously wait until the task completes. This means the current method is "paused" (its state is captured) and the method returns an incomplete task to its caller. Later, when the await expression completes, the remainder of the method is scheduled as a continuation.

You also mentioned a "cooperative block", by which I assume you mean a task that you're Waiting on may execute on the waiting thread. There are situations where this can happen, but it's an optimization. There are many situations where it can't happen, like if the task is for another scheduler, or if it's already started or if it's a non-code task (such as in your code example: Wait cannot execute the Delay task inline because there's no code for it).

You may find my async / await intro helpful.

Using Task.Wait instead of await for async programming

Isn't that technically incorrect then?

No, because it's being very specific. It's not saying that writing an async method that awaits a task is always the same as just synchronously waiting on that task, it's only referring to very specifically the case of an async Main method as an entry point for the application. When you make the Main method async, it just synchronously waits on the returned task, making it functionally equivalent to just synchronously waiting on the task inside the method instead of making the method async only in that one exact situation.

(You could also make an argument that's it's just trying to say that StartNew with the parameters provided and Run are equivalent, and isn't intending to refer to the difference between the method being async versus synchronously waiting.)

why would ASP.Net allow a synchronous Wait on them anyway?

Task wasn't created exclusively to be a representation of work done asynchronously. It was designed to do that and also to synchronously work in parallel using multiple threads. When you're using tasks for asynchrony, you should basically never be using Wait or other synchronous blocking mechanism, but if you're using it to do multithreaded work synchronously, it's useful. You could make a [good] argument that they should have kept those concepts separate, but they didn't, and it's way too late to change it now.

Doesn't that kind of violate their main utility?

Yes, yes it does. Which is why I'm not a fan of this implementation, and would have preferred they'd implemented it differently. But they didn't.

The article should explain the differences between the two calls clearly.

Yes it should.

Task.Wait() deadlocks my application

I have read that it is beneficial to use async/await on networking

Yes, but why?

The answer is: async is beneficial (especially for I/O-based code) so that you free up threads to do other things. In an ASP.NET app, this means you have more threads available to handle incoming requests. Oh, wait...

protected override void Seed(ApplicationDbContext context)

When your application is handling its first request (and seeding the db), there isn't any point in freeing up threads because they won't be able to do anything until after the db is seeded anyway.

So in this situation, you should just synchronously upload.

can someone explain why this is happening

I have a blog post that goes into more detail, but to summarize:

  • await by default will capture a "context", and will resume executing the async method in that context.
  • ASP.NET (currently) has a request context that handles things like HttpContext.Current, the current page culture, etc.
  • The ASP.NET context only allows one thread in at a time.

Thus, when you block a thread in the request context (Wait), the async method cannot complete because it's waiting for that request context to be free. Of course, the Wait is waiting for the async method to complete, so you end up with a deadlock.

DeadLock on task.Wait() with Task which edit UI

You have a deadlock because ping.Wait(); blocks UI thread.

You should wait for task asynchronously using await.

So, if Stop() is event handler then change it to:

public async void Stop() // async added here
{
if (running)
{
tokenSource.Cancel();
await ping; // await here
ping.Dispose();
tokenSource.Dispose();
}

running = false;
}

If it is not:

public async Task Stop() // async added here, void changed to Task
{
if (running)
{
tokenSource.Cancel();
await ping; // await here
ping.Dispose();
tokenSource.Dispose();
}

running = false;
}

As mentioned by @JohnB async methods should have Async suffix so, the method should be named as StopAsync().

Similar problem and solution are explained here - Do Not Block On Async Code

You should avoid synchronous waiting on tasks, so you should always use await with tasks instead of Wait() or Result. Also, as pointed by @Fildor you should use async-await all the way to avoid such situations.

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

You are deadlocking the application because you didn't use async/await or ConfigureAwait(false) but you chose to use Task.Wait and Task.Result instead.

You should know first that Task.Run captures the SynchronizationContext of the thread it is executed on. Then Task.Run runs on a new ThreadPool thread. When completed it will return to the parent thread to continue execution of the remaining code. When returning it will return to the captured SyncronizationContext. You are breaking the concept by using Task.Wait and Task.Result. Both Task members will invoke the Task synchronously, which means the parent thread will block itself until the child thread has completed. The child thread completes but the Task can't return to the captured SynchronizationContext to execute the remaining code (the code after Task.Wait), since the parent thread is still blocking itself by waiting for the task to run to completion.

Because you used Task.Wait in one place and Task.Result in another, you have created two potential deadlock situations:

Let's step through the Function() code:

public Task<ReturnsMessage> Function() {

1) Create a task and start it:

var task = Task.Run(
() =>
{
var result = SyncMethod();
return new ReturnMessage(result);
});

Important things do happen here:

Task.Run captures the current SynchronizationContext and starts execution in the background while the parent thread continues to execute. (If await would've been used here, then the remaining code that follows the await would be enqueued into a continuation queue for later execution. The intend is that the current thread can return (leave the current context) so that it doesn't need to wait and block. Remaining code will be executed by Task once the child thread has run to completion, since it was enqueued in the continuation queue before).

2) task.Wait() waits until the background thread has completed. Waiting means blocking the thread from continue to execute. The call stack is parked. This is equal to synchronization of the background thread, since there is no parallel execution anymore as the parent thread doesn't continue to execute but blocks:

 // Dangerous implementation of a timeout for the executing `task` object
if (task.Wait(delay)) {
return task;
}

Important things do happen here:

task.Wait() blocks the current thread (SynchronizationContext) waiting for the child thread to complete. The child task completes and Task tries to execute the previously enqueued remaining code from the continuation queue in the captured SynchronizationContext. But this context is blocked by the thread waiting for the child task to complete. Potential deadlock situation one.

The following remaining code will be unreachable:

var tcs = new TaskCompletionSource<ReturnMessage>();
tcs.SetCanceled();
return tcs.Task;

async and await was introduced to get rid of the blocking waits. await allows the parent thread to return and continue. The remaining code after await will be executed as continuation in the captured SynchronizationContext.

This is the fix for the first deadlock also using a proper task timeout solution that uses Task.WhenAny (not preferred):

public async Task<ReturnsMessage> FunctionAsync()
{
using (var cancellationTokenSource = new CancellationTokenSource())
{
try
{
var task = Task.Run(
() =>
{
// Check if the task needs to be cancelled
// because the timeout task ran to completion first
cancellationToken.ThrowIfCancellationRequested();

var result = SyncMethod();
return result;
}, cancellationTokenSource.Token);

int delay = 500;
Task timoutTask = Task.Delay(delay, cancellationTokenSource.Token);
Task firstCompletedTask = await Task.WhenAny(task, timoutTask);

if (firstCompletedTask == task)
{
// The 'task' has won the race, therefore
// cancel the 'timeoutTask'
cancellationTokenSource.Cancel();
return await task;
}
}
catch (OperationCanceledException)
{}

// The 'timeoutTask' has won the race, therefore
// cancel the 'task' instance
cancellationTokenSource.Cancel();

var tcs = new TaskCompletionSource<string>();
tcs.SetCanceled();
return await tcs.Task;
}
}

Or the fix for the first deadlock with an alternate and much better timeout approach using the CancellationTokenSouce timeout constructor overload (preferred):

public async Task<ReturnsMessage> FunctionAsync()
{
var timeout = 50;
using (var timeoutCancellationTokenSource = new CancellationTokenSource(timeout))
{
try
{
return await Task.Run(
() =>
{
// Check if the timeout elapsed
timeoutCancellationTokenSource.Token.ThrowIfCancellationRequested();

var result = SyncMethod();
return result;
}, timeoutCancellationTokenSource.Token);
}
catch (OperationCanceledException)
{
var tcs = new TaskCompletionSource<string>();
tcs.SetCanceled();
return await tcs.Task;
}
}
}

The second potential deadlocking code is the consumption of Function():

for (var i = 0; i < maxAttempts; i++)
{
return Function().Result;
}

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.

The reason for the deadlocking is the same as explained before: a blocked SynchronizationContext that prevents the execution of the scheduled continuation.

To fix the second deadlock we can use async/await (preferred) or ConfigreAwait(false):

for (var i = 0; i < maxAttempts; i++)
{
return await FunctionAsync();
}

or ConfigreAwait(false). This approach can be used to force synchronized execution of an asynchronous method:

for (var i = 0; i < maxAttempts; i++)
{
return FunctionAsync().ConfigureAwait(false).GetAwaiter().GetResult();
}

ConfigreAwait(false) instructs the Task to ignore the captured SynchronizationContext and to continue the execeution of the continuation queue on another ThreadPool thread which will never be the parent thread.

Trying to understand Async/Await but seem to get deadlock c#

I had to put await on the CalculateInvoice method for it to move on.

[HttpPost]
[Route("calculateInvoice")]
public async Task<IHttpActionResult> CalculateInvoice([FromBody] InvoiceRequestModel model)
{
model.EmailAddress = AccountHelper.GetLoggedInUsername();
var result = await _paymentHandler.CreatePaymentsAndSendAsEmail(model, true);
if (result == null)
return Conflict();
return Ok(result);
}

async/await deadlock when using WindowsFormsSynchronizationContext in a console app

This happens because the WindowsFormsSynchronizationContext depends on the existence of a standard Windows message loop. A console application does not start such a loop, so the messages posted to the WindowsFormsSynchronizationContext are not processed, the task continuations are not invoked, and so the program hangs on the first await. You can confirm the non-existence of a message loop by querying the boolean property Application.MessageLoop.

Gets a value indicating whether a message loop exists on this thread.

To make the WindowsFormsSynchronizationContext functional you must start a message loop. It can be done like this:

static void Main(string[] args)
{
EventHandler idleHandler = null;
idleHandler = async (sender, e) =>
{
Application.Idle -= idleHandler;
await MyMain(args);
Application.ExitThread();
};
Application.Idle += idleHandler;
Application.Run();
}

The MyMain method is your current Main method, renamed.


Update: Actually the Application.Run method installs automatically a WindowsFormsSynchronizationContext in the current thread, so you don't have to do it explicitly. If you want you can prevent this automatic installation, be configuring the property WindowsFormsSynchronizationContext.AutoInstall before calling Application.Run.

The AutoInstall property determines whether the WindowsFormsSynchronizationContext is installed when a control is created, or when a message loop is started.



Related Topics



Leave a reply



Submit