rspec/rules/S4462/rule.adoc

70 lines
2.6 KiB
Plaintext
Raw Normal View History

2020-12-23 14:59:06 +01:00
Making blocking calls to ``async`` methods transforms something that was intended to be asynchronous into a synchronous block. Doing so can cause deadlocks and unexpected blocking of context threads.
2020-06-30 12:49:37 +02:00
According to the MSDN documentation:
{quote}
2020-12-23 14:59:06 +01:00
The root cause of this deadlock is due to the way ``await`` handles contexts. By default, when an incomplete ``Task`` is awaited, the current “context” is captured and used to resume the method when the ``Task`` completes. This “context” is the current ``SynchronizationContext`` unless its null, in which case its the current ``TaskScheduler``. GUI and ASP.NET applications have a ``SynchronizationContext`` that permits only one chunk of code to run at a time. When the ``await`` completes, it attempts to execute the remainder of the ``async`` method within the captured context. But that context already has a thread in it, which is (synchronously) waiting for the ``async`` method to complete. Theyre each waiting for the other, causing a deadlock.
2020-06-30 12:49:37 +02:00
{quote}
||To Do This …||Instead of This …||Use This||
2020-12-23 14:59:06 +01:00
|Retrieve the result of a background task|``Task.Wait``, ``Task.Result`` or ``Task.GetAwaiter.GetResult``|``await``|
|Wait for any task to complete|``Task.WaitAny``|``await Task.WhenAny``|
|Retrieve the results of multiple tasks|``Task.WaitAll``|``await Task.WhenAll``|
|Wait a period of time|``Thread.Sleep``|``await Task.Delay``|
2020-06-30 12:49:37 +02:00
== Noncompliant Code Example
----
public static class DeadlockDemo
{
private static async Task DelayAsync()
{
await Task.Delay(1000);
}
// This method causes a deadlock when called in a GUI or ASP.NET context.
public static void Test()
{
// Start the delay.
var delayTask = DelayAsync();
// Wait for the delay to complete.
delayTask.Wait(); // Noncompliant
}
}
----
== Compliant Solution
----
public static class DeadlockDemo
{
private static async Task DelayAsync()
{
await Task.Delay(1000);
}
public static async Task TestAsync()
{
// Start the delay.
var delayTask = DelayAsync();
// Wait for the delay to complete.
await delayTask;
}
}
----
== Exceptions
* Main methods of Console Applications are not subject to this deadlock issue and so are ignored by this rule.
2020-12-23 14:59:06 +01:00
* ``Thread.Sleep`` is also ignored when it is used in a non-``async`` method.
* Calls chained after ``Task.Run`` or ``Task.Factory.StartNew`` are ignored because they don't suffer from this deadlock issue
2020-06-30 12:49:37 +02:00
== See
* https://msdn.microsoft.com/en-us/magazine/jj991977.aspx[Async/Await - Best Practices in Asynchronous Programming]