C# DISCUSSION

Why does calling .Result or .Wait() on an async method freeze my WinForms or WPF app?

Started by adelalwaqfi async await deadlockSynchronizationContextTask.ResultConfigureAwaitUI thread
5 replies 248 views 6 participants
Latest activity · 30 Sep 2026

Why does calling .Result or .Wait() on an async method freeze my WinForms or WPF app?

adelalwaqfi C# Forum
#1

I have async Task<string> ReadDeviceAsync(), which awaits a network read. From a button click handler I call it synchronously with var text = ReadDeviceAsync().Result;, because the handler was not async. The window freezes permanently on that line. The same code in a console test program returns normally.

The device does reply, as I can see in a network capture, so why does the call never return in the GUI application, and what is the correct way to call async code from an event handler?

Community replies 5

Re: Why does calling .Result or .Wait() on an async method freeze my WinForms or WPF app?

#2

It is a deadlock between the UI thread and the continuation of your async method. When await meets a task that has not completed, it captures the current SynchronizationContext and returns to the caller. In WinForms and WPF that context represents the UI thread, so when the read finishes, the rest of ReadDeviceAsync is posted to the UI thread's message queue.

But the UI thread is blocked inside .Result, waiting for the task to complete, and the task cannot complete until its continuation runs on the UI thread. Neither side can move. A console application has no synchronization context, so the continuation runs on a thread-pool thread, the task completes and .Result returns.

Re: Why does calling .Result or .Wait() on an async method freeze my WinForms or WPF app?

#3

The correct fix is to be async all the way up. Event handlers are the one place where async void is appropriate: private async void btnRead_Click(object sender, EventArgs e) { btnRead.Enabled = false; try { txtOut.Text = await ReadDeviceAsync(); } catch (Exception ex) { MessageBox.Show(ex.Message); } finally { btnRead.Enabled = true; } }.

While the read is pending the handler has returned, so the UI thread keeps pumping messages and the window stays responsive. After the await, execution resumes on the UI thread, so touching controls is safe. Keep the try/catch: an exception that escapes an async void method cannot be observed by any caller and will normally bring the application down.

Re: Why does calling .Result or .Wait() on an async method freeze my WinForms or WPF app?

#4

In library code, ConfigureAwait(false) is relevant: var n = await stream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false); tells the await not to capture the context, so the continuation runs on a thread-pool thread. If every await inside ReadDeviceAsync and everything it calls does this, a blocking caller no longer deadlocks.

It is good practice in general-purpose libraries, but do not treat it as the cure for blocking. Miss one await and the deadlock is back, and after ConfigureAwait(false) the code is no longer on the UI thread, so it must not touch controls. ASP.NET Core has no synchronization context, so this particular deadlock does not occur there, although blocking on tasks still ties up thread-pool threads.

Re: Why does calling .Result or .Wait() on an async method freeze my WinForms or WPF app?

#5

If the caller truly cannot be made async, for example a synchronous interface you do not control, the least bad option is to start the work on the thread pool so that no UI context is captured: var text = Task.Run(() => ReadDeviceAsync()).Result;. This does not deadlock, but the UI thread is still frozen for the duration, and the async method must not touch UI elements.

Note that .Result and .Wait() wrap failures in an AggregateException, while .GetAwaiter().GetResult() rethrows the original exception; it blocks, and deadlocks, in exactly the same way. For constructors, use an async factory method such as public static async Task<Device> CreateAsync(), or do the work in the form's Load handler.

Re: Why does calling .Result or .Wait() on an async method freeze my WinForms or WPF app?

#6

Two more points. For CPU-heavy work in a handler, such as parsing a large capture file, use await Task.Run(() => Parse(data)); the computation moves to a pool thread and the handler resumes on the UI thread with the result. Do not wrap naturally asynchronous I/O in Task.Run; await it directly.

Give device I/O a timeout so a silent device does not leave the operation pending forever: using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3));, pass cts.Token to the read and catch OperationCanceledException. To confirm the diagnosis, pause in the debugger while the window is frozen: the main thread's call stack shows it waiting inside get_Result, called from your click handler.

TEP COMMUNITY