美文网首页
C#5.0的异步函数async\await

C#5.0的异步函数async\await

作者: 大捕猎店 | 来源:发表于2017-07-30 23:03 被阅读0次

如果需要 I/O 绑定(例如从网络请求数据或访问数据库),则需要利用异步编程。
还可以使用 CPU 绑定代码(例如执行成本高昂的计算),对编写异步代码而言,这是一个不错的方案。
C# 拥有语言级别的异步编程模型,它使你能轻松编写异步代码,而无需应付回叫或符合支持异步的库。 它遵循基于任务的异步模式 (TAP)

异步模型的基本概述

对于 I/O 绑定代码,当你 await 一个操作,它将返回 async 方法中的一个 TaskTask<T>

对于 CPU 绑定代码,当你 await 一个操作,它将在后台线程通过 Task.Run 方法启动。

await 关键字是点睛之笔,因为它暂停对执行 await 的方法的调用方的控制权。 这正是 UI 具有响应性或服务具有灵活性的原因。

I/O 绑定示例:从 Web 服务下载数据

private readonly HttpClient _httpClient = new HttpClient();

downloadButton.Clicked += async (o, e) =>
{
    // This line will yield control to the UI as the request
    // from the web service is happening.
    //
    // The UI thread is now free to perform other work.
    var stringData = await _httpClient.GetStringAsync(URL);
    DoSomethingWithData(stringData);
};

CPU 绑定示例:为游戏执行计算
最佳解决方法是启动一个后台线程,它使用 Task.Run 执行工作,并 await 其结果。 这可确保在执行工作时 UI 能流畅运行。

private DamageResult CalculateDamageDone()
{
    // Code omitted:
    //
    // Does an expensive calculation and returns
    // the result of that calculation.
}


calculateButton.Clicked += async (o, e) =>
{
    // This line will yield control to the UI CalculateDamageDone()
    // performs its work.  The UI thread is free to perform other work.
    var damageResult = await Task.Run(() => CalculateDamageDone());
    DisplayDamage(damageResult);
};

内部原理

异步操作涉及许多移动部分。 若要了解 TaskTask<T> 的内部原理,请参阅深入了解异步

在 C# 方面,编译器将代码转换为状态机,它将跟踪类似以下内容:到达 await 时暂停执行以及后台作业完成时继续执行。

从理论上讲,这是异步的承诺模型的实现。

需了解的要点

  • 异步代码可用于 I/O 绑定和 CPU 绑定代码,但在每个方案中有所不同。
  • 异步代码使用 Task<T>Task,它们是对后台所完成的工作进行建模的构造。
  • async 关键字将方法转换为异步方法,这使你能在其正文中使用 await 关键字。
  • 应用 await 关键字后,它将挂起调用方法,并将控制权返还给调用方,直到等待的任务完成。
  • 仅允许在异步方法中使用 await

识别 CPU 绑定和 I/O 绑定工作

如果你的工作为 I/O 绑定,请使用 asyncawait(而不使用 Task.Run)。 不应使用任务并行库。

如果你的工作为 CPU 绑定,并且你重视响应能力,请使用 asyncawait,并在另一个线程上使用 Task.Run 生成工作。 如果该工作同时适用于并发和并行,则应考虑使用任务并行库

此外,应始终对代码的执行进行测量。 例如,你可能会遇到这样的情况:多线程处理时,上下文切换的开销高于 CPU 绑定工作的开销。 每种选择都有折衷,应根据自身情况选择正确的折衷方案。

等待多个任务完成

你可能发现自己处于需要并行检索多个数据部分的情况。 Task API 包含两种方法(即 Task.WhenAllTask.WhenAny),这些方法允许你编写在多个后台作业中执行非阻止等待的异步代码。

此示例演示如何为一组 User 捕捉 userId 数据

public async Task<User> GetUser(int userId)
{
    // Code omitted:
    //
    // Given a user Id {userId}, retrieves a User object corresponding
    // to the entry in the database with {userId} as its Id.
}

public static Task<IEnumerable<User>> GetUsers(IEnumerable<int> userIds)
{
    var getUserTasks = new List<Task<User>>();

    foreach (int userId in userIds)
    {
        getUserTasks.Add(GetUser(id));
    }

    return await Task.WhenAll(getUserTasks);
}

以下是使用 LINQ 进行更简洁编写的另一种方法:

public async Task<User> GetUser(int userId)
{
    // Code omitted:
    //
    // Given a user Id {userId}, retrieves a User object corresponding
    // to the entry in the database with {userId} as its Id.
}

public static async Task<User[]> GetUsers(IEnumerable<int> userIds)
{
    var getUserTasks = userIds.Select(id => GetUser(id));
    return await Task.WhenAll(getUserTasks);
}

尽管它的代码较少,但在混合 LINQ 和异步代码时需要谨慎操作。 因为 LINQ 使用延迟的执行,因此异步调用将不会像在 foreach() 循环中那样立刻发生,除非强制所生成的序列通过对 .ToList().ToArray() 的调用循环访问。

重要信息和建议

  • async 方法需在其主体中具有 await 关键字,否则它们将永不暂停!
  • 应将Async作为后缀添加到所编写的每个异步方法名称中。
  • async void 应仅用于事件处理程序。
    async void 是允许异步事件处理程序工作的唯一方法,因为事件不具有返回类型(因此无法利用 TaskTask<T>)。 其他任何对 async void 的使用都不遵循 TAP 模型,且可能存在一定使用难度,例如:
    • async void 方法中引发的异常无法在该方法外部被捕获。
    • 十分难以测试 async void 方法。
    • 如果调用方不希望 async void方法是异步方法,则这些方法可能会产生不好的副作用。
      • 在 LINQ 表达式中使用异步 lambda 时请谨慎
        LINQ 中的 Lambda 表达式使用延迟执行,这意味着代码可能在你并不希望结束的时候停止执行。 如果编写不正确,将阻塞任务引入其中时可能很容易导致死锁。 此外,此类异步代码嵌套可能会对推断代码的执行带来更多困难。 Async 和 LINQ 的功能都十分强大,但在结合使用两者时应尽可能小心。

相关文章

  • ES8(一) —— async&await

    目录 async和普通函数的区别 await async/await处理多回调异步 async和await必须配合...

  • C#5.0的异步函数async\await

    如果需要 I/O 绑定(例如从网络请求数据或访问数据库),则需要利用异步编程。还可以使用 CPU 绑定代码(例如执...

  • dart(五)--异步、生成器

    异步 async,await 可以使同步代码异步执行。只有在async的函数里可以使用await。该函数执行到aw...

  • async-await

    1 async-await 1 异步函数 async function async关键字用于声明一个异步函数: a...

  • async await 异步的简单用法

    async 是声明一个异步函数,如 async ()=>{} await 是等待一个异步方法执行,await 执行...

  • C#中await/async闲说

    自从C#5.0增加异步编程之后,异步编程越来越简单,async和await用的地方越来越多,越来越好用,只要用异步...

  • 2020-05-06--Vue之前后端交互6-async,awa

    async,await async:‘异步’ await:‘等待’ async作为一个关键字放到函数前面 任何一个...

  • async和await

    浅谈Async/Await用 async/await 来处理异步 async和await async:声明一个异步...

  • 笔记总结

    一、ES8(async/await)异步函数 ES8: async await 注意:async必须要去修饰一个函...

  • async函数用法

    async await ES2017 标准引入了 async 函数,使得异步操作变得更加方便。async 函数是什...

网友评论

      本文标题:C#5.0的异步函数async\await

      本文链接:https://www.haomeiwen.com/subject/sqtllxtx.html