原文

避免async void

async void异步方法只有一个目的:使得event handler异步可行,也就是说async void只能用于event handler。

async void方法有不同的错误处理机制。当async Task或者async Task<T>方法里面抛出异常时,异常会被捕捉到,并放在Task对象里面。在async void方法里面没有Task对象,因此发生在async void方法里面的异常会直接raised到SynchronizationContext 中。发生在async void方法里面的异常不会被正常捕捉到。

private async void ThrowExceptionAsync()
{
throw new InvalidOperationException();
} public void AsyncVoidExceptions_CannotBeCaughtByCatch()
{
try
{
ThrowExceptionAsync();
}
catch (Exception)
{
// 不会捕捉到这个异常
throw;
}
}

返回Task或者Task<T>的异步方法,可以使用await, Task.WhenAny, Task.WhenAll观察到异步方法是什么时候结束的。async void方法不容易观察到什么时候完成的。async void方法完成后会通知到他们的SynchronizationContext,但是太麻烦了。

Async void方法不利于测试。

死锁

public static class DeadlockDemo
{
private static async Task DelayAsync()
{
await Task.Delay(1000);
} // 在GUI和ASP.NET下这个方法会导致死锁
public static void Test()
{
// Start the delay.
var delayTask = DelayAsync();
// Wait for the delay to complete.
delayTask.Wait();
}
}

死锁的根本原因是await的处理上下文的方式。默认情况下,当一个未完成的Task处于等待时,会捕捉到当前的上下文用于当Task完成时回到该方法。这个上下文就是SynchronizationContext。GUI 和ASP.NET 应用的SynchronizationContext一次只允许一段代码运行。当await完成,它企图在捕捉到的上下文中执行async方法剩下来的代码。但是这个上下文已经有一个线程在跑了,这个线程就是等待这个async方法运行完的方法(在这里就是Test这个同步方法)。这两个方法相互等待对方完成,因此发生了死锁。

控制台应用不会发生死锁。因为它有一个线程池的SynchronizationContext而不是GUI 和ASP.NET 应用一次只允许一段代码运行的SynchronizationContext

解决这个死锁的最好的方案是让Test这个方法也是异步的。控制台方法不能用这个方案,因为Main方法不能是异步的。如果Main方法是异步的,它会在调用的异步方法前返回。

控制台的Main方法是少有的可以调用异步方法的非异步方法。

class Program
{
static void Main()
{
MainAsync().Wait();
} static async Task MainAsync()
{
try
{
// Asynchronous implementation.
await Task.Delay(1000);
}
catch (Exception ex)
{
// Handle exceptions.
}
}
}

Configure Context

回到上面死锁的问题,当一个未完成的Task处于await的时候,默认会捕捉到上下文,捕捉到的这个上下文用于重回异步方法。context的行为会导致另外一个问题---性能问题。As asynchronous GUI applications grow larger, you might find many small parts of async methods all using the GUI thread as their context. This can cause sluggishness as responsiveness suffers from “thousands of paper cuts.”

减少这个导致的性能问题,可以通过ConfigureAwait来实现。

async Task MyMethodAsync()
{
// 代码在最初的上下文中运行
await Task.Delay(1000); // 代码在最初的上下文中运行
await Task.Delay(1000).ConfigureAwait(continueOnCapturedContext: false); // 代码不在最初的上下文中运行了
// 现在的上下文在线程池
}

除了性能外,ConfigureAwait的另外一个重要的作用是可以避免死锁。

[译]Async/Await - Best Practices in Asynchronous Programming的更多相关文章

  1. Async/Await - Best Practices in Asynchronous Programming z

    These days there’s a wealth of information about the new async and await support in the Microsoft .N ...

  2. Async/Await - Best Practices in Asynchronous Programming

    https://msdn.microsoft.com/en-us/magazine/jj991977.aspx Figure 1 Summary of Asynchronous Programming ...

  3. [译]async/await中使用阻塞式代码导致死锁 百万数据排序:优化的选择排序(堆排序)

    [译]async/await中使用阻塞式代码导致死锁 这篇博文主要是讲解在async/await中使用阻塞式代码导致死锁的问题,以及如何避免出现这种死锁.内容主要是从作者Stephen Cleary的 ...

  4. [译]async/await中使用阻塞式代码导致死锁

    原文:[译]async/await中使用阻塞式代码导致死锁 这篇博文主要是讲解在async/await中使用阻塞式代码导致死锁的问题,以及如何避免出现这种死锁.内容主要是从作者Stephen Clea ...

  5. [译]async/await中阻塞死锁

    这篇博文主要是讲解在async/await中使用阻塞式代码导致死锁的问题,以及如何避免出现这种死锁.内容主要是从作者Stephen Cleary的两篇博文中翻译过来. 原文1:Don'tBlock o ...

  6. Best Practices in Asynchronous Programming

    http://blog.stephencleary.com/ http://blogs.msdn.com/b/pfxteam/

  7. 将 async/await 异步代码转换为安全的不会死锁的同步代码

    在 async/await 异步模型(即 TAP Task-based Asynchronous Pattern)出现以前,有大量的同步代码存在于代码库中,以至于这些代码全部迁移到 async/awa ...

  8. Async/Await FAQ

    From time to time, I receive questions from developers which highlight either a need for more inform ...

  9. 异步模式:Callbacks, Promises & Async/Await

    [译]异步JavaScript的演变史:从回调到Promises再到Async/Await https://www.i-programmer.info/programming/theory/8864- ...

随机推荐

  1. mysql 基础sql语句

    1.mysqladmin语句:  # 查看mysql版本 mysqladmin version  # 更改root用户密码 mysqladmin -u root -p原密码 password '新密码 ...

  2. 西湖论剑2019-msc之奇怪的TTL

    msc1给了一串很长的TTL字符,参考一些隐写的文章,猜测是在ttl中藏了信息,题目是这样的 我们截获了一些IP数据报,发现报文头中的TTL值特别可疑,怀疑是通信方嵌入了数据到TTL,我们将这些TTL ...

  3. linux的常用命令介绍

    1.ls  列出当前目录下的所有的文件和文件夹的名称. 参数如下:-a 显示隐藏文件 -l 显示方式为列表 -h 以可读性高的方式输出 eg: ls -lh /logs/tran  目录如果不指定(相 ...

  4. 【Teradata SQL】行转列函数TDStats.udfConcat

    TDstats.udfConcat为Teradata自带UDF,定义如下: show function tdstats.udfconcat; REPLACE FUNCTION tdstats.UDFC ...

  5. 如何通过Git将写好的项目发布到github上

    1.在GitHub上创建新的项目文件 2.创建之后会进入新的页面,看到如下图的内容,将地址记下来 3.打开Git 4.进入项目本地所在目录 5.输入:git init 这个意思是在当前项目的目录中生成 ...

  6. codeforces div2 220 解题

    这套题我只写了a, b, c..  对不起,是我太菜了. A:思路:就是直接简化为一个矩阵按照特定的步骤从一个顶角走到与之对应的对角线上的顶角.如图所示. 解释一下特定的步骤,就像马走日,象走田一样. ...

  7. asp.net core 排序过滤分页组件:sieve(2)表达式树的复习

    在Sieve组件中使用了很多关于表达式树的知识,但在我们日常的工作中写表达式树的机会是非常少的,至少在我的编程生涯中没怎么写过表达式树(可能也就是3,4次).所以,为了能够看懂Sieve里面的源代码, ...

  8. 在Winform开发中使用Grid++报表

    之前一直使用各种报表工具,如RDLC.DevExpress套件的XtraReport报表,在之前一些随笔也有介绍,最近接触锐浪的Grid++报表,做了一些测试例子和辅助类来处理报表内容,觉得还是很不错 ...

  9. Jmeter二次开发代码(3)

    package org.apache.jmeter.functions; import java.io.FileInputStream;import java.io.FileNotFoundExcep ...

  10. (二)jdk8学习心得之Lambda表达式

    二.Lambda表达式 1. 格式 (参数1,参数2,…,参数n)->{方法体} 注意: (参数1,参数2,...,参数n)要与方法接口中的参数一致,但是名字可以不一样. 此外,方法类型接口,有 ...