zoukankan      html  css  js  c++  java
  • C# 异步编程

    如果需要 I/O 绑定(例如从网络请求数据或访问数据库),则需要利用异步编程。 还可以使用 CPU 绑定代码(例如执行成本高昂的计算),对编写异步代码而言,这是一个不错的方案。

    异步模型的基本概述

    异步编程的核心是 Task 和 Task<T> 对象,这两个对象对异步操作建模。 它们受关键字 async 和 await 的支持。 在大多数情况下模型十分简单:

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

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

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

    编写代码前应考虑的两个问题:

    1.你的代码是否会“等待”某些内容,例如数据库中的数据?

    如果答案为“是”,则你的工作是 I/O 绑定。

    2.你的代码是否要执行开销巨大的计算?

    如果答案为“是”,则你的工作是 CPU 绑定。

    如果你的工作为 I/O 绑定,请使用 async 和 await(而不使用 Task.Run)。 不应使用任务并行库。 相关原因在深入了解异步的文章中说明。

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

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

    你可能需要在按下按钮时从 Web 服务下载某些数据,但不希望阻止 UI 线程。 只需执行如下操作即可轻松实现:

    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 绑定示例:为游戏执行计算

    假设你正在编写一个移动游戏,在该游戏中,按下某个按钮将会对屏幕中的许多敌人造成伤害。 执行伤害计算的开销可能极大,而且在 UI 线程中执行计算有可能使游戏在计算执行过程中暂停!

    此问题的最佳解决方法是启动一个后台线程,它使用 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 while CalculateDamageDone()
        // performs its work.  The UI thread is free to perform other work.
        var damageResult = await Task.Run(() => CalculateDamageDone());
        DisplayDamage(damageResult);
    };

    它无需手动管理后台线程,而是通过非阻止性的方式来实现。

    等待多个任务完成

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

    public async Task<User> GetUserAsync(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<IEnumerable<User>> GetUsersAsync(IEnumerable<int> userIds)
    {
        var getUserTasks = new List<Task<User>>();
    
        foreach (int userId in userIds)
        {
            getUserTasks.Add(GetUserAsync(userId));
        }
    
        return await Task.WhenAll(getUserTasks);
    }

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

    public async Task<User> GetUserAsync(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[]> GetUsersAsync(IEnumerable<int> userIds)
    {
        var getUserTasks = userIds.Select(id => GetUserAsync(id));
        return await Task.WhenAll(getUserTasks);
    }

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

    LINQ 中的 Lambda 表达式使用延迟执行,这意味着代码可能在你并不希望结束的时候停止执行。 如果编写不正确,将阻塞任务引入其中时可能很容易导致死锁。 此外,此类异步代码嵌套可能会对推断代码的执行带来更多困难。 Async 和 LINQ 的功能都十分强大,但在结合使用两者时应尽可能小心。

  • 相关阅读:
    项目经理如何管理团队
    正则表达式判断中文字符
    售前工作经验总结
    项目管理知识体系九大知识领域
    项目经理如何调动员工的积极性
    项目经理与客户沟通的宜与忌
    关于CodeSmith生成CSLA代码的联想
    接触CSLA框架的感觉
    C#多线程浅接触二、
    WF工作流开发回顾:介绍
  • 原文地址:https://www.cnblogs.com/zhengzc/p/10631436.html
Copyright © 2011-2022 走看看