task run result deadlock

Await, and UI, and deadlocks! Oh my!

Stephen Toub - MSFT

January 13th, 2011 2 3

It’s been awesome seeing the level of interest developers have had for the Async CTP and how much usage it’s getting.  Of course, with any new technology there are bound to be some hiccups.  One issue I’ve seen arise now multiple times is developers accidentally deadlocking their application by blocking their UI thread, so I thought it would be worthwhile to take a few moments to explore the common cause of this and how to avoid such predicaments.

At its core, the new async language functionality aims to restore the ability for developers to write the sequential, imperative code they’re used to writing, but to have it be asynchronous in nature rather than synchronous.  That means that when operations would otherwise tie up the current thread of execution, they’re instead offloaded elsewhere, allowing the current thread to make forward progress and do other useful work while, in effect, asynchronously waiting for the spawned operation to complete.  In both server and client applications, this can be crucial for application scalability, and in client applications in particular it’s also really useful for responsiveness.

Most UI frameworks, such as Windows Forms and WPF, utilize a message loop to receive and process incoming messages.  These messages include things like notifications of keys being typed on a keyboard, or buttons being clicked on a mouse, or controls in the user interface being manipulated, or the need to refresh an area of the window, or even the application sending itself a message dictating some code to be executed.  In response to these messages, the UI performs some action, such as redrawing its surface, or changing the text being displayed, or adding items to one of its controls., or running the code that was posted to it.  The “message loop” is typically literally a loop in code, where a thread continually waits for the next message to arrive, processes it, goes back to get the next message, processes it, and so on.  As long as that thread is able to quickly process messages as soon as they arrive, the application remains responsive, and the application’s users remain happy.  If, however, processing a particular message takes too long, the thread running the message loop code will be unable to pick up the next message in a timely fashion, and responsiveness will decrease.  This could take the form of pauses in responding to user input, and if the thread’s delays get bad enough (e.g. an infinite delay), the application “hanging”.

In a framework like Windows Forms or WPF, when a user clicks a button, that typically ends up sending a message to the message loop, which translates the message into a call to a handler of some kind, such as a method on the class representing the user interface, e.g.:

private void button1_Click(object sender, RoutedEventArgs e) {     string s = LoadString();     textBox1.Text = s; }

Here, when I click the button1 control, the message will inform WPF to invoke the button1_Click method, which will in turn run a method LoadString to get a string value, and store that string value into the textBox1 control’s Text property.  As long as LoadString is quick to execute, all is well, but the longer LoadString takes, the more time the UI thread is delayed inside button1_Click, unable to return to the message loop to pick up and process the next message.

To address that, we can choose to load the string asynchronously, meaning that rather than blocking the thread calling button1_Click from returning to the message loop until the string loading has completed, we’ll instead just have that thread launch the loading operation and then go back to the message loop.  Only when the loading operation completes will we then send another message to the message loop to say “hey, that loading operation you previously started is done, and you can pick up where you left off and continue executing.”  Imagine we had a method:

public Task<string> LoadStringAsync();

This method will return very quickly to its caller, handing back a .NET Task<string> object that represents the future completion of the asynchronous operation and its future result.  At some point in the future when the operation completes, the task object will be able to hand out the operations’ result, which could be the string in the case of successful loading, or an exception in the case of failure.  Either way, the task object provides several mechanisms to notify the holder of the object that the loading operation has completed.  One way is to synchronously block waiting for the task to complete, and that can be accomplished by calling the task’s Wait method, or by accessing its Result, which will implicitly wait until the operation has completed… in both of these cases, a call to these members will not complete until the operation has completed.  An alternative way is to receive an asynchronous callback, where you register with the task a delegate that will be invoked when the task completes.  That can be accomplished using one of the Task’s ContinueWith methods.  With ContinueWith, we can now rewrite our previous button1_Click method to not block the UI thread while we’re asynchronously waiting for the loading operation to complete:

private void button1_Click(object sender, RoutedEventArgs e) {     Task<string> s = LoadStringAsync();     s.ContinueWith(delegate { textBox1.Text = s.Result; }); // warning: buggy }

This does in fact asynchronously launch the loading operation, and then asynchronously run the code to store the result into the UI when the operation completes.  However, we now have a new problem.  UI frameworks like Windows Forms, WPF, and Silverlight all place a restriction on which threads are able to access UI controls, namely that the control can only be accessed from the thread that created it.  Here, however, we’re running the callback to update the Text of textBox1on some arbitrary thread, wherever the Task Parallel Library (TPL) implementation of ContinueWith happened to put it.  To address this, we need some way to get back to the UI thread.  Different UI frameworks provide different mechanisms for doing this, but in .NET they all take basically the same shape, a BeginInvoke method you can use to pass some code as a message to the UI thread to be processed:

private void button1_Click(object sender, RoutedEventArgs e) {     Task<string> s = LoadStringAsync();     s.ContinueWith(delegate     {         Dispatcher.BeginInvoke(new Action(delegate         {             textBox1.Text = s.Result;         }));     }); }

The .NET Framework further abstracts over these mechanisms for getting back to the UI thread, and in general a mechanism for posting some code to a particular context, through the SynchronizationContext class.  A framework can establish a current context, available through the SynchronizationContext.Current property, which provides a SynchronizationContext instance representing the current environment.  This instance’s Post method will marshal a delegate back to this environment to be invoked: in a WPF app, that means bringing you back to the dispatcher, or UI thread, you were previously on.  So, we can rewrite the previous code as follows:

private void button1_Click(object sender, RoutedEventArgs e) {     var sc = SynchronizationContext.Current;     Task<string> s = LoadStringAsync();     s.ContinueWith(delegate     {         sc.Post(delegate { textBox1.Text = s.Result; }, null);     }); }

and in fact this pattern is so common, TPL in .NET 4 provides the TaskScheduler.FromCurrentSynchronizationContext() method, which allows you to do the same thing with code like:

private void button1_Click(object sender, RoutedEventArgs e) {     LoadStringAsync().ContinueWith(s => textBox1.Text = s.Result,         TaskScheduler.FromCurrentSynchronizationContext()); }

As mentioned, this works by “posting” the delegate back to the UI thread to be executed.  That posting is a message like any other, and it requires the UI thread to go through its message loop, pick up the message, and process it (which will result in invoking the posted delegate).  In order for the delegate to be invoked then, the thread first needs to return to the message loop, which means it must leave the button1_Click method.

Now, there’s still a fair amount of boilerplate code to write above, and it gets orders of magnitude worse when you start introducing more complicated flow control constructs, like conditionals and loops.  To address this, the new async language feature allows you to write this same code as:

private async void button1_Click(object sender, RoutedEventArgs e) {     string s = await LoadStringAsync();     textBox1.Text = s; }

For all intents and purposes, this is the same as the previous code shown, and you can see how much cleaner it is… in fact, it’s close to identical  in the code required to our original synchronous implementation.  But, of course, this one is asynchronous: after calling LoadStringAsync and getting back the Task<string> object, the remainder of the function is hooked up as a callback that will be posted to the current SynchronizationContext in order to continue execution on the right thread when the loading is complete.  The compiler is layering on some really helpful syntactic sugar here.

Now things get interesting. Let’s imagine LoadStringAsync is implemented as follows:

static async Task<string> LoadStringAsync() {     string firstName = await GetFirstNameAsync();     string lastName = await GetLastNameAsync();     return firstName + ” ” + lastName; }

LoadStringAsync is implemented to first asynchronously retrieve a first name, then asynchronously retrieve a last name, and then return the concatenation of the two.  Notice that it’s using “await”, which, as pointed out previously, is similar to the aforementioned TPL code that uses a continuation to post back to the synchronization context that was current when the await was issued.  So, here’s the crucial point: for LoadStringAsync to complete (i.e. for it to have loaded all of its data and returned its concatenated string, completing the task it returned with that concatenated result), the delegates it posted to the UI thread must have completed.  If the UI thread is unable to get back to the message loop to process messages, it will be unable to pick up the posted delegates that resulted from the asynchronous operations in LoadStringAsync completing, which means the remainder of LoadStringAsync will not run, which means the Task<string> returned from LoadStringAsync will not complete.  It won’t complete until the relevant messages are processed by the message loop.

With that in mind, consider this (faulty) reimplementation of button1_Click:

private void button1_Click(object sender, RoutedEventArgs e) {     Task<string> s = LoadStringAsync();     textBox1.Text = s.Result; // warning: buggy }

There’s an exceedingly good chance that this code will hang your application.  The Task<string>.Result property is strongly typed as a String, and thus it can’t return until it has the valid result string to hand back; in other words, it blocks until the result is available.  We’re inside of button1_Click then blocking for LoadStringAsync to complete, but LoadStringAsync’s implementation depends on being able to post code asynchronously back to the UI to be executed, and the task returned from LoadStringAsync won’t complete until it does.  LoadStringAsync is waiting for button1_Click to complete, and button1_Click is waiting for LoadStringAsync to complete. Deadlock!

This problem can be exemplified easily without using any of this complicated machinery, e.g.:

private void button1_Click(object sender, RoutedEventArgs e) {     var mre = new ManualResetEvent(false);     SynchronizationContext.Current.Post(_ => mre.Set(), null);     mre.WaitOne(); // warning: buggy }

Here, we’re creating a ManualResetEvent, a synchronization primitive that allows us to synchronously wait (block) until the primitive is set.  After creating it, we post back to the UI thread to set the event, and then we wait for it to be set.  But we’re waiting on the very thread that would go back to the message loop to pick up the posted message to do the set operation.  Deadlock.

The moral of this (longer than intended) story is that you should not block the UI thread.  Contrary to Nike’s recommendations, just don’t do it.  The new async language functionality makes it easy to asynchronous wait for your work to complete.  So, on your UI thread, instead of writing:

Task<string> s = LoadStringAsync(); textBox1.Text = s.Result; // BAD ON UI

you can write:

Task<string> s = LoadStringAsync(); textBox1.Text = await s; // GOOD ON UI

Or instead of writing:

Task t = DoWork(); t.Wait(); // BAD ON UI
Task t = DoWork(); await t; // GOOD ON UI

This isn’t to say you should never block.  To the contrary, synchronously waiting for a task to complete can be a very effective mechanism, and can exhibit less overhead in many situations than the asynchronous counterpart.  There are also some contexts where asynchronously waiting can be dangerous. For these reasons and others, Task and Task<TResult> support both approaches, so you can have your cake and eat it too.  Just be cognizant of what you’re doing and when, and don’t block your UI thread.

(One final note: the Async CTP includes the TaskEx.ConfigureAwait method.  You can use this method to suppress the default behavior of marshaling back to the original synchronization context.  This could have been used, for example, in the LoadStringAsync method to prevent those awaits from needing to return to the UI thread.  This would not only have prevented the deadlock, it would have also resulted in better performance, because we now no longer need to force execution back to the UI thread, when nothing in that method actually needed to run on the UI thread.)

Stephen Toub - MSFT Partner Software Engineer, .NET

Discussion is closed. Login to edit/delete existing comments.

I must admin that this article(and the series of articles you have written on TPL) is one of the most comprehensive blogs I have ever read regarding TPL.

I have one quick question regarding this statement that you have made:

There’s an exceedingly good chance that this code will hang your application. The Task.Result property is strongly typed as a String, and thus it can’t return until it has the valid result string to hand back; in other words, it blocks until the result is available. We’re inside of button1_Click then blocking for LoadStringAsync to complete, but LoadStringAsync’s implementation depends on being able to post code asynchronously back to the UI to be executed, and the task returned from LoadStringAsync won’t complete until it does. LoadStringAsync is waiting for button1_Click to complete, and button1_Click is waiting for LoadStringAsync to complete. Deadlock!

So the main problem as to why Deadlock occurs is because the UI Thread cannot process the message posted to the message pump right? Or is it because LoadStringAsync() method cannot even post message to the message pump because the UI thread is blocked by the caller waiting for LoadStringAsync() to complete. If my understanding is correct, the DeadLock happens because the UI thread cannot process the message posted to the message pump which means that LoadStringAsync did post to the message pump but that message cannot be picked up by the UI thread(as its waiting) and thus LoadStringAsync cannot mark itself as complete?

Microsoft employee

> So the main problem as to why Deadlock occurs is because the UI Thread cannot process the message posted to the message pump right?

Correct. The UI thread is blocked waiting for the task to complete, and the task won’t complete until the UI thread pumps messages, which won’t happen because the UI thread is blocked.


A Tour of Task, Part 6: Results

The task members discussed in this blog post are concerned with retrieving results from the task. Once the task completes, the consuming code must retrieve the results of the task. Even if the task has no result, it’s important for the consuming code to examine the task for errors so it knows whether the task completed successfully or failed.

The Result member only exists on the Task<T> type; it does not exist on the Task type (which represents a task without a result value).

Like Wait , Result will synchronously block the calling thread until the task completes. This is generally not a good idea for the same reason it wasn’t a good idea for Wait : it’s easy to cause deadlocks .

Furthermore, Result will wrap any task exceptions inside an AggregateException . This usually just complicates the error handling.

Speaking of exceptions, there’s a member specifically just for retrieving the exceptions from a task:

Unlike Result and Wait , Exception will not block until the task completes; if called while the task is still in progress, it will just return null . If the task completes successfully or is cancelled, then Exception will still return null . If the task is faulted, then Exception will return the task’s exceptions wrapped in an AggregateException . Again, this usually just serves to complicate the error handling.


The GetAwaiter member was added to Task and Task<T> in .NET 4.5, and it’s available as an extension method on .NET 4.0 using the Microsoft.Bcl.Async NuGet package. Normally, the GetAwaiter method is just used by await , but it is possible to call it yourself:

The code above will synchronously block until the task completes. As such, it is subject to the same old deadlock problems as Wait and Result . However, it will not wrap the task exceptions in an AggregateException .

The code above will retrieve the result value from a Task<T> . The same code pattern can also be applied to Task (without a result value); in this case “GetResult” actually means “check the task for errors”:

In general, I try my best to avoid synchronously blocking on an asynchronous task. However, there are a handful of situations where I do violate that guideline. In those rare conditions, my preferred method is GetAwaiter().GetResult() because it preserves the task exceptions instead of wrapping them in an AggregateException .

Of course, await is not a member of the task type; however, I feel it’s important to remind today’s readers that the best way of retrieving results from a Promise Task is to merely use await . await retrieves task results in the most benign manner possible: await will asynchronously wait (not block); await will return the result (if any) for a successful task; and await will (re-)throw exceptions for a failed task without wrapping them in an AggregateException .

In short, await should be your go-to option for retrieving task results. The vast majority of the time, await should be used instead of Wait , Result , Exception , or GetAwaiter().GetResult() .

  • Constructors
  • AsyncState and CreationOptions
  • Continuations
  • Delegate Tasks
  • Promise Tasks

task run result deadlock

  • Async/await Intro
  • There Is No Thread
  • Don't Block on Async Code
  • React/Redux TodoMVC
  • A Tour of Task
  • Task.Run Etiquette
  • Task.Run vs. BackgroundWorker
  • TCP/IP .NET Sockets FAQ
  • Managed Services
  • IDisposable and Finalizers
  • Option Parsing

Deadlocks with async and await explained

Preventing deadlocks with async and await

Back in 2012 the .NET Framework 4.5 introduced asynchronous programming with async and await. Nowadays I regularly see it being used, but I can see that it just isn’t understood well.

Let me first say that async and await seems extremely simple when you look at it at a glance, but it really is not that easy. There are many things about async and await, that I could write about. View the async/await series here .

A sample deadlock

The following code is a example for Windows Forms, but the same principles apply to any UI application and to any ASP.NET application.

This simple example below retrieves a string from a website, and displays the returned string in a textbox. This will result in a deadlock.

The deadlock explained

The “ async ” and “ await ” keywords do not create any additional threads. Async methods are intended to be non-blocking operations. The method runs on the current “ synchronization context ” and uses time on the thread only when the method is active. You should use “ Task.Run ” to execute CPU-bound work in a background thread, but you don’t need any background thread to wait for results, e.g. a http-request. It’s like time-sharing a single thread.

Simply put, a “ synchronization context ” represents a location “where” code might be executed. Every thread can have a “ synchronization context ” instance associated with it.

What the current “ synchronization context ” is, can vary.

  • On a UI thread, it is a UI context.
  • In an ASP.NET request, it is an ASP.NET request context.
  • Otherwise, it is usually a thread pool context.

When you await an async method or, to be more precise, any awaitable, the awaitable will capture the current “ synchronization context ”. Later when awaitable completes, the remainder of the async method will be executed on the “ context ” that was captured before the “ await ” returned.

The problem occurs when you have a single “ synchronization context ”, like in our Winforms example above. But the same would apply to any ASP.NET application. Let me explain what happens from the top-level method.

  • The top-level method calls the GetTextAsync with the UI/ASP.NET context.
  • GetTextAsync starts the http-request by calling GetStringAsync.
  • GetStringAsync returns a started but uncompleted task.
  • GetTextAsync awaits the task returned by GetStringAsync.
  • GetTextAsync returns a started but uncompleted task.
  • The top-level method keeps the “ synchronization context ” locked while waiting on the task returned by GetTextAsync.
  • After a while, the http-request will complete and the task returned by GetStringAsync will complete.
  • Now the remainder of the GetTextAsync is now ready to run, and it waits for the context to be available so it can execute in the context.
  • … and it waits, and waits, and waits. While we realize that the “ synchronization context ” will never become available.
  • Deadlock : The top-level method is blocking the “ synchronization context ”, waiting for GetTextAsync to complete, while the GetTextAsync is waiting for the context to be free so it can complete.

The solutions

The solution is simple, use async all the way down. Never block on tasks yourself.

Another solution is to call “ ConfigureAwait(false) ” on the task of the underlying method, to prevent the continuation of the task on the original context captured.

If you really cannot use async all the way, then you could use “ Task.Run(…) ” to execute the async method in a separate thread.

Like in my previous article : Never force a wait on an async method. You should use async all the way down. Alternatively, either use “ ConfigureAwait(false) ” in your underlying methods/libraries or use a separate thread to execute the async method.

I think async and await is really great. It makes writing asynchronous code relatively easy. The construction improves the readability of code and the code becomes easy to follow, because the compiler hides all complicated stuff from you. But don’t forget, asynchronous code is not simple and you should really understand what async and await actually do.

Check out the complete async/await series here .

Posts you may also enjoy

C# Deadlocks in Depth - Part 1

task run result deadlock

For me, multi-threading programming is one of the most fun things I do as a developer. It’s fun because it’s hard and challenging. And I also get a particular sense of satisfaction when solving deadlocks (you’ll see what I mean).

This series will go through understanding deadlocks, show common deadlock types, how to solve them, how to debug them and best practices to avoid them. In Part 1 , I’ll show one of the easiest deadlock scenarios, how to debug it in Visual Studio and finally how to fix it. We’ll cover some of the basics, but I’ll move quickly to more advanced topics as well.

Defining a Deadlock

A deadlock in C# is a situation where two or more threads are frozen in their execution because they are waiting for each other to finish. For example, thread A is waiting on lock_1 that is held by thread B. Thread B can’t finish and release lock_1 because it waits on lock_2 , which is held by thread A. Too confusing? I’ll show you an example in a moment, but first let’s talk about Locks .

Brief explanation of Locks

A Lock is a way for us to synchronize between Threads. A lock is a shared object that can be Acquired by a Thread, and also Released . Once Acquired, other threads can be made to halt execution until the lock is Released. A lock is usually placed around a critical section, where you want to allow a single Thread at a time. For example:

Without a lock , 2 threads might enter the critical section, ending up with 2 instances of our Singleton. The example uses the lock statement . A lock statement uses Monitor.Enter and Monitor.Exit under the hood. Another way to achieve locking is to use a Mutex or a Semaphore . We might talk about those as well.

Deadlock example 1: The Nested-Lock

Explanation of the code:

  • Two objects are created for lock purposes. In C#, any object can be used as a lock.
  • Task.Run starts 2 Tasks, which are run by 2 Threads on the Thread-Pool .
  • The first Thread acquires lock1 and sleeps for 1 second. The second acquires lock2 and also sleeps for a second. Afterward, thread 1 waits for lock2 to be released and thread 2 waits for lock1 to be released. So they both wait indefinitely and result in a Deadlock .
  • Task.WaitAll(task1, task2) waits on the method’s Thread until both Tasks are finished, which never happens. This makes it a 3-Thread deadlock. The Console print is: Starting…

Debugging a Deadlock

You can see the deadlock in the debugger easily, once you know what to look for. In the example above, running the code in Visual Studio results in a hang. Hit on the Debug | Break All (Ctrl + Alt + Break), then go to Debug | Windows | Threads . You’ll see the following:

task run result deadlock

This is how a deadlock looks like in debugging. As you can see, the Main Thread (on the left) is stuck on Task.WaitAll() . The other 2 Threads are stuck on the inner lock statement. In fact, to recognize deadlocks, you should look for Threads stuck on one of the following:

  • lock statements
  • WaitOne() methods when working with AutoResetEvent, Mutex, Semaphore, EventWaitHandle.
  • WaitAll() and WaitAny() when working with Tasks.
  • Join() when working with Threads.
  • .Result and . GetAwaiter().GetResult() when working with Tasks.
  • Dispatcher.Invoke() when working in WPF.

When you see the debugger’s execution point stuck on any of the above, there’s a big chance you have a deadlock. We’ll see in following parts of this series examples of deadlocks with most, if not all of those statements.

Solving the Nested-Lock Deadlock

Now that you recognized the deadlock, it’s time to solve it. There are several ways to go about it. The obvious one being: don’t use a lock within a lock. That’s not always possible though. For example, let’s say each lock represents an Account . We want to use the lock on each operation on the account. When we do an operation with both accounts (like a Transfer), we want to lock both of them.

Solution #1 – Nest the locks in the same order

If we nest the locks in the same order, there’s not going to be a deadlock. Let’s change the code in our example a bit to mimic the Account Locking problem:

Now, to solve it by nesting the locks in the same order, we need to change:

Since the outer lock is going to be the same in all transfers, there’s no deadlock. One of the Threads is going to wait in the outer lock until the first Thread finishes, then go on.

Solution #2 – Use Timeout

Another way to solve this is to use a Timeout when waiting for a lock to be released. If the lock isn’t released within some time, the operation is canceled. It can be moved back to an operation queue or something similar and executed at a later time. Or just try again after a small delay.

Remember, we said that the lock statement is actually Monitor.Enter() and Monitor.Exit() under the hood. When using those methods, it’s possible to pass a Timeout as a parameter. This means that if the locked failed to Acquire within the Timeout , False is returned.

In our case, we try to acquire both locks. If acquiring fails, we simply release both and try again. Theoretically, it might be possible with this method to always fail to do an operation – When both Threads acquire the outer lock at exactly the same time, then fail to acquire the inner lock. But, in practice it’s pretty much impossible. The thread-switching mechanism will be at different times each time.

It’s worth mentioning that modern applications with Transfer type of operations can avoid locks entirely by using patterns like Event Sourcing .

In this part, we talked a bit about locks , saw one type of deadlock, how to debug it and 2 ways to solve it.

As a best practice, be very suspicious when using locks inside other locks. This might be missed since the entire method can be within a locked context. Another best practice is if you do need to use a lock, place as little code as possible inside.

This is going to be a 2-part or 3-part series, I’m not sure yet. In the next part(s), I’ll show some of the more common deadlocks and more sophisticated ways to debug them.

Here’s a little spoiler deadlock from the next part of the series:

Did you get that particular satisfaction that comes along with solving deadlock? If you did, check out the C# Deadlocks in Depth Part 2 . And I’d love it if you subscribe to the blog and be notified of more in-depth C# articles. Happy coding.

task run result deadlock

Welcome to my blog! I’m a software developer, C# enthusiast, author, and a blogger. I write about C#, .NET, memory management, and performance. Working at Microsoft, but all opinions in this blog are my own. More about me →

Check out my book Practical Debugging for .NET Developers to become an expert problem solver

Practical Debugging .NET

Recent Posts

  • How culture and structure in big tech (GAFAM) show in their products
  • Changing TypeScript library functions while keeping backwards compatibility
  • Recapping C# and .NET in 2023: Announcements, Conferences, and best Blog Posts
  • Development slowness in big and legacy applications [and how to hurry it up]
  • How to Debug LINQ queries in C#
  • Premature Infrastructure is the Root of All Evil
  • Declutter Your Work Day: 9 Tips to Manage your Tasks Without Stress
  • 7 Command Prompt Techniques in Windows You Should Know
  • All the Possible Ways to Debug Node.js
  • How Would Steve Jobs Fare as a Software Engineer?

Navigation Menu

Search code, repositories, users, issues, pull requests..., provide feedback.

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly.

To see all available qualifiers, see our documentation .

  • Notifications

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement . We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Task.Run inside a test will result in deadlock if a control was created previously #2123


costin-zaharia commented Apr 13, 2017


jnm2 commented Apr 13, 2017

Sorry, something went wrong.

costin-zaharia commented Apr 13, 2017 • edited

Jnm2 commented apr 13, 2017 • edited.


  • 👍 1 reaction


CharliePoole commented Apr 13, 2017


jnm2 commented Mar 25, 2018

Jnm2 commented apr 9, 2018.

No branches or pull requests


This browser is no longer supported.

Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.

Debug a deadlock in .NET Core

  • 8 contributors

This article applies to: ✔️ .NET Core 3.1 SDK and later versions

In this tutorial, you'll learn how to debug a deadlock scenario. Using the provided example ASP.NET Core web app source code repository, you can cause a deadlock intentionally. The endpoint will stop responding and experience thread accumulation. You'll learn how you can use various tools to analyze the problem, such as core dumps, core dump analysis, and process tracing.

In this tutorial, you will:

  • Investigate an app that has stopped responding
  • Generate a core dump file
  • Analyze process threads in the dump file
  • Analyze call stacks and sync blocks
  • Diagnose and solve a deadlock


The tutorial uses:

  • .NET Core 3.1 SDK or a later version
  • Sample debug target - web app to trigger the scenario
  • dotnet-trace to list processes
  • dotnet-dump to collect, and analyze a dump file

Core dump generation

To investigate application unresponsiveness, a core dump or memory dump allows you to inspect the state of its threads and any possible locks that may have contention issues. Run the sample debug application using the following command from the sample root directory:

To find the process ID, use the following command:

Take note of the process ID from your command output. Our process ID was 4807 , but yours will be different. Navigate to the following URL, which is an API endpoint on the sample site:


The API request to the site will stop responding. Let the request run for about 10-15 seconds. Then create the core dump using the following command:

Analyze the core dump

To start the core dump analysis, open the core dump using the following dotnet-dump analyze command. The argument is the path to the core dump file that was collected earlier.

Since you're looking at a potentially unresponsive application, you want an overall feel for the thread activity in the process. You can use the threads command as shown below:

The output shows all the threads currently running in the process with their associated debugger thread ID and operating system thread ID. Based on the output, there are over 300 threads.

The next step is to get a better understanding of what the threads are currently doing by getting each thread's callstack. The clrstack command can be used to output callstacks. It can either output a single callstack or all the callstacks. Use the following command to output all the callstacks for all the threads in the process:

A representative portion of the output looks like:

Observing the callstacks for all 300+ threads shows a pattern where a majority of the threads share a common callstack:

The callstack seems to show that the request arrived in our deadlock method that in turn makes a call to Monitor.ReliableEnter . This method indicates that the threads are trying to enter a monitor lock. They're waiting on the availability of the lock. It's likely locked by a different thread.

The next step then is to find out which thread is actually holding the monitor lock. Since monitors typically store lock information in the sync block table, we can use the syncblk command to get more information:

The two interesting columns are MonitorHeld and Owning Thread Info . The MonitorHeld column shows whether a monitor lock is acquired by a thread and the number of waiting threads. The Owning Thread Info column shows which thread currently owns the monitor lock. The thread info has three different subcolumns. The second subcolumn shows operating system thread ID.

At this point, we know two different threads (0x5634 and 0x51d4) hold a monitor lock. The next step is to take a look at what those threads are doing. We need to check if they're stuck indefinitely holding the lock. Let's use the setthread and clrstack commands to switch to each of the threads and display the callstacks.

To look at the first thread, run the setthread command, and find the index of the 0x5634 thread (our index was 28). The deadlock function is waiting to acquire a lock, but it already owns the lock. It's in deadlock waiting for the lock it already holds.

The second thread is similar. It's also trying to acquire a lock that it already owns. The remaining 300+ threads that are all waiting are most likely also waiting on one of the locks that caused the deadlock.

  • dotnet-counters to check managed memory usage
  • dotnet-dump to collect and analyze a dump file
  • dotnet/diagnostics

What diagnostic tools are available in .NET Core

Coming soon: Throughout 2024 we will be phasing out GitHub Issues as the feedback mechanism for content and replacing it with a new feedback system. For more information see: https://aka.ms/ContentUserFeedback .

Submit and view feedback for

Additional resources


  1. Deadlock: What it is, How to Detect, Handle and Prevent?

    task run result deadlock

  2. Explain Different Methods of Handling the Deadlock

    task run result deadlock

  3. Deadlock in Operating System

    task run result deadlock

  4. Understanding Async, Avoiding Deadlocks in C#

    task run result deadlock

  5. SQL Deadlocks: Why They Happen & How To Avoid Them

    task run result deadlock

  6. Code Studio

    task run result deadlock


  1. L32: Deadlock

  2. 4.03

  3. what is a deadlock in os

  4. deadlock prevention in os

  5. Deadlock in Operating System

  6. deadlock characterization part 1


  1. 'await' works, but calling task.Result hangs/deadlocks

    No deadlock issues will occur due to the use of Task.Run. public String GetSqlConnString(RubrikkUser user, RubrikkDb db) { // deadlock if called from threadpool, // works fine on UI thread, works fine from console main return Task.Run(() => GetSqlConnStringAsync(user, db)).Result; }

  2. Use Task.Run () in synchronous method to avoid deadlock waiting on

    After reading Stephen Cleary's comment in another question that Task.Run() always schedules on the thread pool even async methods, it made me think. In .NET 4.5 in ASP.NET or any other synchronization context that schedules tasks to the current thread / same thread, if I have an asynchronous method: private async Task MyAsyncMethod() { ...

  3. An async/await example that causes a deadlock

    In these single-threaded synchronization contexts, it's easy to deadlock yourself. If you spawn off a task from a single-threaded context, then wait for that task in the context, your waiting code may be blocking the background task. public ActionResult ActionAsync() {. // DEADLOCK: this blocks on the async task. var data = GetDataAsync().Result;

  4. Understanding Async, Avoiding Deadlocks in C#

    Task.Wait () does. That would be the end of story but sometimes it cannot be avoided, and it's not the only case. Deadlock might also be cause by other sort of blocking code, waiting for ...

  5. Don't Block on Async Code

    Result. ToString ();}} This code will also deadlock. For the same reason. What Causes the Deadlock. Here's the situation: remember from my intro post that after you await a Task, when the method continues it will continue in a context. In the first case, this context is a UI context (which applies to any UI except Console applications). In ...

  6. Async/Await

    The first problem is task creation. Obviously, an async method can create a task, and that's the easiest option. If you need to run code on the thread pool, use Task.Run. If you want to create a task wrapper for an existing asynchronous operation or event, use TaskCompletionSource<T>.

  7. Consuming the Task-based Asynchronous Pattern

    When you use the Task-based Asynchronous Pattern (TAP) to work with asynchronous operations, you can use callbacks to achieve waiting without blocking. For tasks, this is achieved through methods such as Task.ContinueWith. Language-based asynchronous support hides callbacks by allowing asynchronous operations to be awaited within normal control ...

  8. Advanced Tips for Using Task.Run With Async/Await

    In the previous guide in this series we saw why Task.Run is mainly useful for CPU-bound code. In exploring that topic it became clear that, although you can use Task.Run with other types of operations, it may not be the best use of system resources. We also saw how easy it is to await a call to Task.Run.But that's certainly not all there is to it.

  9. Await, and UI, and deadlocks! Oh my!

    The new async language functionality makes it easy to asynchronous wait for your work to complete. So, on your UI thread, instead of writing: Task<string> s = LoadStringAsync (); textBox1.Text = s.Result; // BAD ON UI. you can write: Task<string> s = LoadStringAsync (); textBox1.Text = await s; // GOOD ON UI.

  10. A Tour of Task, Part 6: Results

    T result = task.GetAwaiter().GetResult(); The code above will synchronously block until the task completes. As such, it is subject to the same old deadlock problems as Wait and Result. However, it will not wrap the task exceptions in an AggregateException. The code above will retrieve the result value from a Task<T>.

  11. Deadlocks with async and await explained ∼ Keuper ICT

    The deadlock explained. The "async" and "await" keywords do not create any additional threads.Async methods are intended to be non-blocking operations. The method runs on the current "synchronization context" and uses time on the thread only when the method is active.You should use "Task.Run" to execute CPU-bound work in a background thread, but you don't need any background ...

  12. C# Deadlocks in Depth

    Explanation: The is a simple WPF application; OnButtonClick is an event-handler of a Button Click, that executes on the UI Thread; Task.Run() executes work on a ThreadPool Thread. Dispatcher.Invoke() is a WPF method that synchronously executes work on the UI Thread. It queues work on the Dispatcher-Queue and waits for it to finish..Wait() waits for the task to finish, so it keeps the UI Thread ...

  13. Task.Run Method (System.Threading.Tasks)

    The Run (Action, CancellationToken) method is a simpler alternative to the TaskFactory.StartNew (Action, CancellationToken) method. It creates a task with the following default values: Its CreationOptions property value is TaskCreationOptions.DenyChildAttach. It uses the default task scheduler.

  14. How to justify using await instead of .Result() or .Wait() in .NET Core?

    The lack of synchronisation context in dotnetcore might save you from unexpected deadlocks, but it also means you have to worry about thread starvation. Everytime you block a thread with task.Wait() or task.Result() thats one less Thread that your app could be using to do stuff with. Using await frees up that Thread to be used on other tasks.

  15. C# Deadlocks in Depth

    Task.Run starts 2 Tasks, which are run by 2 Threads on the Thread-Pool. The first Thread acquires lock1 and sleeps for 1 second. The second acquires lock2 and also sleeps for a second. Afterward, thread 1 waits for lock2 to be released and thread 2 waits for lock1 to be released. So they both wait indefinitely and result in a Deadlock.

  16. Task.Run inside a test will result in deadlock if a control was created

    Hi, I'm not sure whether an issue with NUnit but the following test will always result in a deadlock: [Test] public async Task Deadlock() { new Control(); await Task.Run(() => { int x = 42; }); } Version used: 3.6.1 Thanks, Costin

  17. Debugging deadlock

    Prerequisites. The tutorial uses:.NET Core 3.1 SDK or a later version; Sample debug target - web app to trigger the scenario; dotnet-trace to list processes; dotnet-dump to collect, and analyze a dump file; Core dump generation. To investigate application unresponsiveness, a core dump or memory dump allows you to inspect the state of its threads and any possible locks that may have contention ...

  18. Why do Task.Wait and Task.Result even exist? : r/csharp

    Both Task.Wait and Task.Result are blocking and may also cause deadlocks and on top of that they also wrap exceptions in an AggregateException . Now if you are in a situation where you can't use async/await and you have to do sync over async, the preferred way to do it seems to be Task.GetAwaiter().GetResult(); which can still cause deadlocks ...

  19. c#

    As such instead of adjusting all classes up the tree to return Task at certain points I simply just performed a .Wait or a .Result on a task. I know that .Wait or .Result holds the thread and also creates a new one to run the task but I don't understand how that results in such dramatic execution swings.

  20. In the context of ASP.NET, why doesn't Task.Run(...).Result deadlock

    Result by itself isn't going to cause a deadlock. A deadlock is when two parts of the code are both waiting for each other. A Result is just one wait, so it can be part of a deadlock, but it doesn't necessarily always cause a deadlock.. In the standard example, the Result waits for the task to complete while holding onto the context, but the task can't complete because it's waiting for the ...

  21. c#

    Great question. When you do Task.Run, the delegate that you pass to it will be run on the threadpool, but importantly here for you, not with a SynchronizationContext.This is what would usually give you a deadlock, when you are blocking on the result of some code, which itself has exclusive access to that context.

  22. c#

    I think your problem is that you're sequentially awaiting every single task. So when you have 100 tasks, you're effectivly firing the second one after the first finishes.. What you seem to need instead, is to fire all these tasks simultaneously and wait for all of them to finish. To achieve that, add every task to a List<Task> and use await Task.WhenAll: ...