I have been working around this problem for a while, but now I would really like to understand what goes wrong. I have a rather simple application (it's a turtoise SVN plugin for youtrack, but I can reproduce the problem with a trivial winforms app).
I have an async method ResolveIssue
public async Task<bool> ResolveIssue(Issue issue, int revision, string[] pathList)
{
await Task.Delay(1000);
return true;
}
All I have to do to create a deadlock is call this async method in a Button event handler, and call Task.Wait or Task.Result, like this
private void buttonOk_Click(object sender, System.EventArgs e)
{
var asyncResolvedIssue = api.ResolveIssue(issue, revision, pathList);
if (asyncResolvedIssue.Result) {} // <== deadlock!
}
Now I understand it's rather weird to have an async method and actively wait for it, but why would it generate a deadlock?!
Your problem is because you are blocking the UI thread when you call .Result and you told the continuation after Task.Delay to run on the UI thread. So you are blocking the UI waiting for a task that is blocked on waiting for the UI to become free, a classic deadlock.
Two solutions. First make the button click async too.
private async void buttonOk_Click(object sender, System.EventArgs e)
{
var asyncResolvedIssue = api.ResolveIssue(issue, revision, pathList);
if (await asyncResolvedIssue) {} // <== no deadlock!
}
Event handlers are the only place you are allowed to do async void.
The other option is tell Task.Delay it does not need to have the rest of its function run on the UI thread by setting ConfigureAwait(bool) to false.
public async Task<bool> ResolveIssue(Issue issue, int revision, string[] pathList)
{
await Task.Delay(1000).ConfigureAwait(false);
return true;
}
Now the line of code after the Task.Delay will run on a threadpool thread instead of the UI thread and will not be blocked by the fact that the UI thread is currently blocked.
Related
I wrote a simple console application to test ConfigureAwait(false) for fixing the problem of calling an async function in a synchronized way. This is how I simulate an event and trying to call an async function in a sync way.
public static class Test2
{
private static EventHandler<int> _somethingEvent;
public static void Run()
{
_somethingEvent += OnSomething;
_somethingEvent(null, 10);
Console.WriteLine("fetch some other resources");
Console.ReadLine();
}
private static void OnSomething(object sender, int e)
{
Console.WriteLine($"passed value : {e}");
FakeAsync().Wait();
}
private static async Task FakeAsync()
{
await Task.Delay(2000).ConfigureAwait(false);
Console.WriteLine($"task completed");
}
}
the output of the code is like this:
passed value : 10
task completed
fetch some other resources
And when I change the OnSomething function to async void like this:
private static async void OnSomething(object sender, int e)
{
Console.WriteLine($"passed value : {e}");
await FakeAsync();
}
The output will change to this:
passed value : 10
fetch some other resources
task completed
You can see fetch some other resources is occurred before task completed section and this is my desired result.
My question is how can I achieve the same result by calling the async function synchronized like the first signature I wrote for the OnSomething function?
How could I use ConfigureAwait(false) to help me in this situation? Or maybe I am not using it properly.
I wrote a simple console application to test ConfigureAwait(false) for fixing the problem of calling an async function in a synchronized way.
There's a couple of problems with this.
ConfigureAwait(false) is not a "fix" for the "problem" of calling an asynchronous function synchronously. It is one possible hack that works in some situations, but it's not generally recommended because you have to use ConfigureAwait(false) everywhere in the entire transitive closure of that function - including second- and third-party calls - and if you just miss one, there is still the possibility of a deadlock. The best solution for the problem of calling an asynchronous function from a synchronous function is to change the calling function to be asynchronous (go "async all the way"). Sometimes this isn't possible and you need a hack, but there is no hack that works in every scenario.
The deadlock issue you're trying to avoid has two ingredients: blocking a thread on a task, and a single-threaded context. Console applications do not have a single-threaded context, so in a Console application it's as though all your code uses ConfigureAwait(false) everywhere.
My question is how can I achieve the same result by calling the async function synchronized like the first signature I wrote for the OnSomething function?
The calling code will either block on the asynchronous code (e.g., Wait), or it will not (e.g., await). You can't block on the asynchronous code and continue executing. The calling thread must either continue executing or block; it can't do both.
It's because when you call _somethingEvent(null, 10);, OnSomething isn't being awaited, so the following Console.WriteLine("fetch some other resources"); will be allowed to run straight after await FakeAsync(); is executed.
If you don't need to await the completion of OnSomething before continuing, just do a fire and forget:
private static void OnSomething(object sender, int e)
{
Console.WriteLine($"passed value : {e}");
Task.Run(() => FakeAsync());
}
I have the following methods:
public override bool SyncNavigationMethod()
{
AsyncShowDialog().Wait();
return true;
}
public Task AsyncShowDialog()
{
//code to show dialog and process user input from the dialog.
}
what happens is that the app goes into a deadlock when SyncNavigationMethod is called.
My guess is that it runs on the MainThread, who is waiting for AsyncShowDialog to complete, and the dialog that is shown cannot call it's callback because it's the MainThread that calls it. And so, the main thread waits to itself and it's a deadlock.
More information:
runtime - UWP.
device: phone (as described above) desktop (the app freezes before the dialog is shown).
I have tried to use .Wait() \ .Result on AsyncShowDialog.
I have tried to use a ManualResetEvent after calling the AsyncShowDialog to block the code from progressing while AsyncShowDialog is running.
Am I right suspecting a deadLock?
How can I call AsyncShowDialog from SyncNavigationMethod without this deadlock?
Thanks.
I will start with one of the best blog post by Stephen Cleary, which explains, how the synchronous blocking of an asynchronous call leads to deadlock and that's true for all systems except Console, which doesn't have a UI context thread:
Now regarding your code and points:
And so, the main thread waits to itself and it's a deadlock.
Yes it does, you have correctly understood it's a deadlock, due to Mainthread blocking itself
Wait, Result and ManualResetEvent, they all synchronously block the Async code, thus the same result
Possible solution:
If the call is a Rest API or a web based call, then make the everything Async and IIS will ensure that it can return post top level Async-Await execution, something like:
public async override Task<bool> SyncNavigationMethod()
{
await AsyncShowDialog();
return true;
}
public async Task AsyncShowDialog()
{
await SomeOperation();
}
Return value of Task<bool> would be automatically unwrapped
Other option:
If you cannot make the SyncNavigationMethod() Async, then try this:
public override bool SyncNavigationMethod()
{
List<Task> taskList = new List<Task>();
taskList.Add(AsyncShowDialog());
Task.WhenAll(taskList.ToArray());
return true;
}
public async Task AsyncShowDialog()
{
await SomeOperation();
}
I'm trying to figure out what's wrong with the code below. I thought that using async and await lets me forget about GUI issues such as freezing because some long code is blocking the main thread.
After I click the button, the GUI is responsive until the call to longRunningMethod, as shown below:
private async void openButton_Click(object sender, RoutedEventArgs e)
{
//doing some usual stuff before calling downloadFiles
Task<int> result = await longRunningMethod(); // async method
//at this point GUI becomes unresponsive
//I'm using the result here, so I can't proceed until the longRunningMethod finishes
}
I can't proceed until the method finishes, because I need the result. Why this code freezes my app?
The problem is within longRunningMethod.
What the code is probably doing is some CPU-bound or blocking operation.
If you want to run some CPU-bound code on a background thread, you have to do so explicitly; async won't jump threads automatically:
int result = await Task.Run(() => longRunningMethod());
Note that if longRunningMethod is CPU-bound, it should have a synchronous - not asynchronous - signature.
If longRunningMethod is not CPU-bound (i.e., it's currently blocking), then you need to change the blocking method calls within longRunningMethod to be asynchronous, and call them via await. Then you can make longRunningMethod asynchronous and call it via await as well:
int result = await longRunningMethodAsync();
Given the following code, why does ask.WhenAny never return when provided with a Task.Delay of 1 second? Technically I'm not sure if it does return after a extended amount of time, but it doesn't after 15 seconds or so after which I manually kill the process. According to the documentation I shouldn't be required to manually start the delayTask, and in fact I receive a exception if I try to do so manually.
The code is being called from the UI thread when a user selects a context menu item in a WPF application, although it works fine if I have the click method specified for the context menu item run this code in a new thread.
public void ContextMenuItem_Click(object sender, RoutedEventArgs e)
{
...
SomeMethod();
...
}
public void SomeMethod()
{
...
SomeOtherMethod();
....
}
public void SomeOtherMethod()
{
...
TcpClient client = Connect().Result;
...
}
//In case you're wondering about the override below, these methods are in
//different classes i've just simplified things here a bit so I'm not posting
//pages worth of code.
public override async Task<TcpClient> Connect()
{
...
Task connectTask = tcpClient.ConnectAsync(URI.Host, URI.Port);
Task delayTask = Task.Delay(1000);
if (await Task.WhenAny(connectTask, delayTask) == connectTask)
{
Console.Write("Connected\n");
...
return tcpClient;
}
Console.Write("Timed out\n");
...
return null;
}
If I change ContextMenuItem_Click to the following it works fine
public void ContextMenuItem_Click(object sender, RoutedEventArgs e)
{
...
new Thread(() => SomeMethod()).Start();
...
}
I predict that further up your call stack, you're calling Task.Wait or Task<T>.Result. This will cause a deadlock that I explain in full on my blog.
In short, what happens is that await will (by default) capture the current "context" and use that to resume its async method. In this example, the "context" is the WPF UI context.
So, when your code does its await on the task returned by WhenAll, it captures the WPF UI context. Later, when that task completes, it will attempt to resume on the UI thread. However, if the UI thread is blocked (i.e., in a call to Wait or Result), then the async method cannot continue running and will never complete the task it returned.
The proper solution is to use await instead of Wait or Result. This means your calling code will need to be async, and it will propagate through your code base. Eventually, you'll need to decide how to make your UI asynchronous, which is an art in itself. At least to start with, you'll need an async void event handler or some kind of an asynchronous MVVM command (I explore async MVVM commands in an MSDN article). From there you'll need to design a proper asynchronous UI; i.e., how your UI looks and what actions it permits when asynchronous operations are in progress.
I have a console application with the following code
static void Main(string[] args) {
Method();
Console.ReadKey();
}
private static void Method() {
Task t = DoSomething();
t.Wait();
Console.WriteLine("Finished");
}
private static async Task DoSomething() {
await Task.Run(() =>
Thread.Sleep(1000));
}
Everything works exactly as I expect it to and the console displays "Finished" one second after running the program. When I move the same code into a windows application then I find that the program just stops
private void button1_Click(object sender, EventArgs e) {
Task t = DoSomething();
t.Wait();
MessageBox.Show("Finished");
}
private static async Task DoSomething() {
await Task.Run(() =>
Thread.Sleep(1000));
}
The debugger shows the current line of execution is t.Wait(), even after the Task in the DoSomething method has been run.
Is there something different that I need to do in a windows application that I don't need to do in a console application? Or am I fundamentally misunderstanding something?
That's something that happens quite often. You're causing a deadlock.
To put it simply, frameworks like WinForms and WPF "enforce" a thread synchronization context, which means that whenever you launch a new task, the rest of your code will continue on the same thread as it started. This is done to ensure that, for example, code that started on the UI thread will continue running on the same thread after the task returns.
Because you're blocking on the task (with the Wait method), and the task is trying to return a value to that blocking thread, both threads go into a deadlock state.
This behaviour doesn't happen in a console application because no thread synchronization context is enforced, the continuation of a task can run on a completely different third thread.
Stephen Cleary explains this very well here: http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html