一直对c#中async/await的用法模模糊糊,不太清晰,今天写了一下Demo彻底明确一下async/await的用法,以免因为对其不了解而对后期的业务产生影响(比如事务导致的锁表等等)。

1. 首先,async/await 一般是成对出现才有意义。其意义在于可以等待异步操作完成后继续顺序执行,而不是异步操作还没处理完成主线程就进行了下一步。

假设,我们现在要模拟简单的下载场景,首先用户点击下载,那么就调用DownloadHandle方法(异步)进行下载,然后通知用户下载完成。其使用 async/await  的区别如下:

(1)使用 async/await 的情况:

internal class Program
{
static void Main(string[] args)
{
DownloadHandle();
Console.ReadLine();
}
/// <summary>
/// 正常使用async/await时,符合正常的业务逻辑:
/// 1. 通知用户下载开始
/// 2. 异步下载
/// 3. 等待异步下载完成后给用户提示下载完成
/// </summary>
public static async void DownloadHandle()
{
Console.WriteLine("下载开始!->主线程ID:" + Thread.CurrentThread.ManagedThreadId);
await Download();
Console.WriteLine("下载完成!->主线程ID:" + Thread.CurrentThread.ManagedThreadId); }
/// <summary>
/// 下载
/// </summary>
/// <returns></returns>
public static Task Download()
{
return Task.Run(() =>
{
Console.WriteLine("下载线程ID:->" + Thread.CurrentThread.ManagedThreadId);
Console.WriteLine("10%");
Console.WriteLine("30%");
Console.WriteLine("50%");
Console.WriteLine("60%");
Console.WriteLine("80%");
Console.WriteLine("99%");
Console.WriteLine("100%");
});
}
}

结果如下:

 可以看到,即时下载使用了异步(线程ID不同也表明了当前使用了异步),业务逻辑最终还是按照我们的需求,按顺序正序执行了。

(2)不使用async/await的情况:

internal class Program
{
static void Main(string[] args)
{
DownloadHandle();
Console.ReadLine();
}
/// <summary>
/// 不适用async/await时,则代码执行顺序时混乱的,不符合业务逻辑:
/// 1. 通知用户下载开始
/// 2. 提示下载完成
/// 3. 开始下载
/// </summary>
public static void DownloadHandle()
{
Console.WriteLine("下载开始!->主线程ID:" + Thread.CurrentThread.ManagedThreadId);
Download();
Console.WriteLine("下载完成!->主线程ID:" + Thread.CurrentThread.ManagedThreadId); }
/// <summary>
/// 下载
/// </summary>
/// <returns></returns>
public static Task Download()
{
return Task.Run(() =>
{
Console.WriteLine("下载线程ID:->" + Thread.CurrentThread.ManagedThreadId);
Console.WriteLine("10%");
Console.WriteLine("30%");
Console.WriteLine("50%");
Console.WriteLine("60%");
Console.WriteLine("80%");
Console.WriteLine("99%");
Console.WriteLine("100%");
});
}
}

结果如下:

可以看到,代码执行顺序混乱了,“下载完成” 跑到了 “下载线程ID” 前面去了,完全没有按照我们预期的顺序执行。

2. 如果可以await的方法不进行await,那将会怎样呢?

(1)如果被调用的异步方法内部使用了Task.Run,那结果可参考我们1中进行讲述的结果。开发者可根据实际需要来进行调用,如果异步方法的调用结果与其上下文逻辑没有严格的执行要求,则可以不进行await(比如记录日志等等)。反之,则需要加await。

(2)如果被调用的异步方法内部只是返回了Task.CompletedTask,即时使用了await/async实际上还是等于同步执行,如下图。

internal class Program
{
static void Main(string[] args)
{
DownloadHandle();
Console.ReadLine();
}
/// <summary>
/// 模拟下载
/// </summary>
public static async void DownloadHandle()
{
Console.WriteLine("下载开始!->主线程ID:" + Thread.CurrentThread.ManagedThreadId);
await Download();
Console.WriteLine("下载完成!->主线程ID:" + Thread.CurrentThread.ManagedThreadId); }
/// <summary>
/// 下载
/// </summary>
/// <returns></returns>
public static Task Download()
{
Console.WriteLine("下载线程ID:->" + Thread.CurrentThread.ManagedThreadId);
Console.WriteLine("10%");
Console.WriteLine("30%");
Console.WriteLine("50%");
Console.WriteLine("60%");
Console.WriteLine("80%");
Console.WriteLine("99%");
Console.WriteLine("100%");
return Task.CompletedTask;
}

结果如图:

可以看到,即使DonwloadHandle方法使用了await/async,还是进行了同步执行,并没有异步效果(可从所有线程ID相同看出)

3. 小技巧: 异步方法的返回值类型一般都是Task或者Task<T>类型的,当返回值为Task时(即方法的返回值类型为void),我们可以直接return Task.Run(()=>{})(以下第一段代码),而不必await Task.Run(()=>{})(以下第二段代码),这样也可从一定程度上提高代码执行效率。

/// <summary>
/// 下载
/// </summary>
/// <returns></returns>
public static Task Download()
{
return Task.Run(() =>
{
Console.WriteLine("下载线程ID:->" + Thread.CurrentThread.ManagedThreadId);
Console.WriteLine("10%");
Console.WriteLine("30%");
Console.WriteLine("50%");
Console.WriteLine("60%");
Console.WriteLine("80%");
Console.WriteLine("99%");
Console.WriteLine("100%");
});
}
/// <summary>
/// 下载
/// </summary>
/// <returns></returns>
public static async Task Download()
{
await Task.Run(() =>
{
Console.WriteLine("下载线程ID:->" + Thread.CurrentThread.ManagedThreadId);
Console.WriteLine("10%");
Console.WriteLine("30%");
Console.WriteLine("50%");
Console.WriteLine("60%");
Console.WriteLine("80%");
Console.WriteLine("99%");
Console.WriteLine("100%");
});
}

以上,只是作者的个人理解,请大神勿喷,如有错误,欢迎指正,谢谢!

关于C#中async/await的用法的更多相关文章

  1. 关于C#中async/await中的异常处理(下)-(转载)

    上一篇文章里我们讨论了某些async/await的用法中出现遗漏异常的情况,并且谈到该如何使用WhenAll辅助方法来避免这种情况.WhenAll辅助方法将会汇总一系列的任务对象,一旦其中某个出错,则 ...

  2. C#中async/await中的异常处理

    在同步编程中,一旦出现错误就会抛出异常,我们可以使用try-catch来捕捉异常,而未被捕获的异常则会不断向上传递,形成一个简单而统一的错误处理机制.不过对于异步编程来说,异常处理一直是件麻烦的事情, ...

  3. 关于C#中async/await中的异常处理(上)-(转载)

    在同步编程中,一旦出现错误就会抛出异常,我们可以使用try…catch来捕捉异常,而未被捕获的异常则会不断向上传递,形成一个简单而统一的错误处理机制.不过对于异步编程来说,异常处理一直是件麻烦的事情, ...

  4. [翻译] Python 3.5中async/await的工作机制

    Python 3.5中async/await的工作机制 多处翻译出于自己理解,如有疑惑请参考原文 原文链接 身为Python核心开发组的成员,我对于这门语言的各种细节充满好奇.尽管我很清楚自己不可能对 ...

  5. 关于C#中async/await中的异常处理(上)

    关于C#中async/await中的异常处理(上) 2012-04-11 09:15 by 老赵, 17919 visits 在同步编程中,一旦出现错误就会抛出异常,我们可以使用try…catch来捕 ...

  6. C# 中 async/await 调用传统 Begin/End 异步方法

    最近在改进园子的图片上传程序,希望实现用户上传图片时同时将图片文件保存在三个地方:1)服务器本地硬盘:2)又拍云:3)阿里云OSS.并且在保存时使用异步操作. 对于异步保存到本地硬盘,只需用 Stea ...

  7. async & await 的用法

    async 和 await 出现在C# 5.0之后,给并行编程带来了不少的方便,特别是当在MVC中的Action也变成async之后,有点开始什么都是async的味道了.但是这也给我们 编程埋下了一些 ...

  8. C# 异步操作 async await 的用法

    1. async与 await 成对出现 async 在方法前使用 ,方法体面面用  await . 2. 使用async 和await定义异步方法不会创建新线程. 3.await 后面一定是一个扫行 ...

  9. promise和async/await的用法

    promise和async都是做异步处理的, 使异步转为同步 1.promise 它和Promise诞生的目的都是为了解决“回调地狱”, promise使用方法: <button @click= ...

  10. async await的用法

    const fs = require('fs'); const readFile = function (fileName) { return new Promise(function (resolv ...

随机推荐

  1. mindxdl---common--test_tools.go

    // Copyright (c) 2021. Huawei Technologies Co., Ltd. All rights reserved.// Package common define co ...

  2. Java对象拷贝原理剖析及最佳实践

    作者:宁海翔 1 前言 对象拷贝,是我们在开发过程中,绕不开的过程,既存在于Po.Dto.Do.Vo各个表现层数据的转换,也存在于系统交互如序列化.反序列化. Java对象拷贝分为深拷贝和浅拷贝,目前 ...

  3. Chrome 103支持使用本地字体,纯前端导出PDF优化

    在前端导出PDF,解决中文乱码一直是一个头疼的问题.要解决这个问题,需要将ttf等字体文件内容注册到页面PDF生成器中.但是之前网页是没有权限直接获取客户机器字体文件,这时就需要从服务器下载字体文件或 ...

  4. fiddler提示"The system proxy was changed,click to reenable fiddler capture"的解决方法

    之前用fiddler 一直都是正常的,但是过了几个月再次使用的时候没几秒钟就提示:The system proxy was changed,click to reenable fiddler capt ...

  5. Android 内存缓存框架 LruCache 的实现原理,手写试试?

    本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问. 前言 大家好,我是小彭. 在之前的文章里,我们聊到了 LRU 缓存淘汰算法,并且分析 Java 标准库中支持 ...

  6. 同时容器,k8s和docker区别是什么? 如何简单理解k8s和docker

    1.k8s是一个开源的容器集群管理系统,可以实现容器集群的自动化部署.自动扩缩容.维护等功能. 2.Docker是一个开源的应用容器引擎,开发者可以打包他们的应用及依赖到一个可移植的容器中,发布到流行 ...

  7. 4.1IDA基础设置--《恶意代码分析实战》

    1.加载一个可执行文件 ① 选项一:当加载一个文件(如PE文件),IDA像操作系统加载器一样将文件映射到内存中. ② 选项三:Binary File:将文件作为一个原始的二进制文件进行反汇编,例如文件 ...

  8. jmeter websocket 接口测试环境准备

    1.下载jdk并进行安装配置环境 2.下载jmeter,解压可直接使用,无需安装 3.进入下载地址下载plugins-manager.jar 插件 4.将下载好plugins-manager.jar ...

  9. Vue element 自定义表单验证(验证手机号)

    <el-form :model="ruleForm" status-icon :rules="rules" ref="ruleForm" ...

  10. java反射基础知识整理

    目录 1.反射机制的作用 2.获取一个类的实例 3.使用Class.forName()方法加载类的静态代码块 4.获取配置文件的路径 5.java反编译 5.1.获取类中的成员变量 5.2.通过类名反 ...