首页 > 编程语言 >.net 非阻塞的异步编程 及 线程调度过程

.net 非阻塞的异步编程 及 线程调度过程

时间:2024-11-19 17:01:22浏览次数:1  
标签:异步 Task await 线程 async net 编程

本文主要分为三个部分:

1、语法格式

2、线程调度情况

3、编程注意事项

* 阅读提示 :鼠标悬停在 章节标题 上可见 文章目录

 

 

异步编程(Task Asynchronous Programming,TAP),一种编程模式(Task-based Asynchronous Pattern)。

TAP 是 .NET 中推荐的异步编程模式,基于 Task 和 Task<TResult> 类型,用于表示异步。

异步编程一般应对两种场景,一是 I/O 绑定,当需要网络连接(连接数据库或读写到文件系统等)等耗时长的任务;二是 CPU 绑定,需要耗时长的计算。

 

 

1、简单的语法格式

.net 一直在为开发人员简化和标准化异步的写法,从 .net 4.5 开始就已经支持使用 aysnc 和 await 的关键字。

异步的语法格式如下:

 

private async Task<TResult> DoSomeStuffAsync(..)

{

     ..

    await ..

    ..

}

 

l  关键词 async 本身不具备什么意义,只是装饰,当方法冠以 async 关键词,方法体内允许使用 await

l  await 是标记需要等待的地方,但其本质并非阻塞线程。

l  “非阻止操作”:指当运行到 await 时,会把当前线程返回到上一级调用者继续执行,如果没有上一级调用者,则该线程当场释放。

 

非阻止操作

阻止操作

备注

任一任务完成

await Task.WhenAny

Task.WaitAny

非阻塞:线程遇到 await 时会返回上一层调用者继续执行,如果没有上一级调用者,则释放该线程;

阻塞:线程在等待期间不能执行其他任务,也不释放线程,硬等

所有任务完成

await Task.WhenAll

Task.WaitAll

等待一段时间

await Task.Delay

Thread.Sleep

 

l  异步方法返回的值总是 Task 的实例,可以是 Task 类型或 Task<TResult>,其中 TResult 是执行的方法的返回类型

l  如果直接拿异步方法的结果,形如 var obj = GetSomethingAsync(),这个 obj 是一个 Task 对象,其中 obj.AsyncStatus,obj.Result 可见执行情况

l  await + 执行异步方法,形如 await GetSomethingAsync() 会得到 TResult 实例

l  直接使用 task.Result 得到的任务结果其状态是未知的,应该使用 await task,保证任务是 Completed 的

l  一般地,异步方法的名字需要添加后缀 Async,以便于写代码的时候区分开同步方法和异步方法

l  等待异步任务的执行过程中,如果其中发生了错误,该异步任务的外层 try catch 会捕捉到,它也是一种任务结果

 

 

 

2、异步运行机制,线程调度

观察以下代码,思考一下控制台会输出什么?

    public static async Task Main(string[] args)
    {
        logMessage("Main <<----");
        var task = GetUrlContentLengthAsync();
        logMessage("Main ---->>");
        await task;
        logMessage("ALL COMPLETED");
    }

    static async Task<int> GetUrlContentLengthAsync()
    {
        logMessage("GetUrlContentLengthAsync start ");

        using var client = new HttpClient();
        Task<string> getStringTask = client.GetStringAsync("https://learn.microsoft.com/dotnet");

        // Do some independent work..

        var contents = await getStringTask;

        logMessage("GetUrlContentLengthAsync end ");
        return contents.Length;
    }

    static void logMessage(string msg)
    {
        Console.WriteLine($"{DateTime.Now.ToString("MM-dd HH:mm:ss.fff")} [{Thread.CurrentThread.ManagedThreadId}] {msg}");
    }

 

打印结果:

11-18 18:34:34.563 [1] Main <<----
11-18 18:34:34.632 [1] GetUrlContentLengthAsync start
11-18 18:34:34.810 [1] Main ---->>
11-18 18:34:37.283 [7] GetUrlContentLengthAsync end
11-18 18:34:37.286 [7] ALL COMPLETED

 

先分析一下 GetUrlContentLengthAsync 这个异步方法,简单归纳会存在以下步骤:

  1. httpClient.GetStringAsync 发起 HTTP 请求,并立即返回一个未完成的任务。
  2. await 关键字会暂停 GetStringAsync 方法的执行,并将控制权返回给调用方。
  3. 任务调度器通过操作系统的通知机制来监听 HTTP 请求的响应。
  4. 操作系统在后台监控 I/O 操作的状态,并在操作完成时通知应用程序。
  5. 任务调度器随后选择一个可用的线程来继续执行 异步方法的剩余部分(await 之后)。

 

线程运行过程如下:

 

* HTTP 请求是一个 I/O 操作,操作系统会通过 I/O 完成端口(IOCP)来处理这些操作。

* IOCP 是一种高效的机制,用于处理异步 I/O 操作。它允许操作系统在 I/O 操作完成时通知应用程序。

 

 

 

3、注意事项

如果调用了 异步方法,一定要 await 任务执行结果

反面示例:

    public static async Task Main(string[] args)
    {
        logMessage("Main <<----");

        // fault example: if do not wait the result then we will do not know what happened on it
        GetSomeStuff();

        logMessage("Main ---->>");
    }

这个异步任务已经在执行,但是却没有后续处理,不知道是成功或是失败,又或者一直在执行没有办法停止,这是危险的。

 

如果要使用 task.Result ,此前一定要 await

await task 得到的是 completed async status 的 result,而直接使用 task.Result 的方式拿结果并不保证这个任务是完成的。

 

是否线程安全

异步编程的机制,允许到正在处理一个请求时,同时存在多个线程在处理操作,如果在对同一个对象做写入操作,这是危险的。

所以并行时的任务最好是没有关系的。

如果使用锁,需要考虑是否会导致死锁的问题。

 

异步编程可能会增加代码复杂度,慎重取舍

异步编程并不一定带来性能提升,毕竟上下文切换也是有开销的,对于简单的任务可能一条线做完的方式更合适。

 

 

 

 

 

 

 

Reference:

[1] The Task Asynchronous Programming (TAP) model with async and await" - C# | Microsoft Learn

[2] Asynchronous programming scenarios - C# | Microsoft Learn

[3] Asynchronous programming - C# | Microsoft Learn

 

标签:异步,Task,await,线程,async,net,编程
From: https://www.cnblogs.com/carmen-019/p/18555115

相关文章

  • C++编程:通过多线程与协程优化阻塞型任务的调度性能
    文章目录0.引言1.多线程VS多线程+协程1.1示例1:使用传统的多线程进行矩阵乘法1.2.示例2:使用协程优化阻塞型任务3.分析与对比0.引言我们知道:多线程:适用于处理计算密集型任务或IO操作较少的场景,但会因为线程切换和创建销毁的开销而影响性能。协程:适用于处......
  • .NET Core 特性(Attribute)底层原理浅谈
    简介烂大街的资料不再赘述,简单来说就是给代码看的注释Attribute的使用场景Attribute不仅仅局限于C#中,在整个.NET框架中都提供了非常大的拓展点,任何地方都有Attribute的影子编译器层比如Obsolete,ConditionalC#层GET,POST,Max,Range,RequireCLRVM层StructLayout,DllImp......
  • 【Linux】线程ID与互斥、同步(锁、条件变量)
      ......
  • 如何在Ubuntu 20.04 LTS上安装Dotnet Core?
    在本教程中,我们将向您展示如何在香港服务器的Ubuntu20.04LTS系统上安装DotnetCore。.NETCore是一个免费的开源软件框架和开源软件框架。它是由Microsoft开发的。它是由Microsoft开发的。.NETCore是一个非常强大的框架。它通常用于开发Web应用程序。步骤1.首先,通过apt在......
  • 数据通信的基础概念,串行、并行、半双工、全双工、同步异步的区分与定义
    在通信领域中,串行、并行、半双工、全双工、同步和异步是描述数据传输方式和特性的重要概念。以下是对这些通信方式的区分与定义(本文结合原子教程以及自己的一些学习笔记综合而成):按照数据通信方式可以分为串行、并行通信:串行通信:数据逐位按照顺序依次传输并行通信:数据各位通......
  • .NET +Vue 开发的高级报表工具
    前言本文介绍一款基于.NET6开发的高级报表工具。该工具支持多种数据源(如数据库、Excel文件、API服务等),并具备强大的数据处理和可视化功能。通过内置的集合函数和类Excel界面,用户可以轻松设计和生成复杂的报表,满足各类业务需求。项目介绍CellReport是一款专为复杂统计报......
  • net MVC ActionResultObjectValueAttribute
    MVC返回结果IActionResultIActionResult派生类如下......
  • Asp.net Core Hosted Service(托管服务) Timer (定时任务)
    HostedService托管服务要继承IHostedService或继承BackgroundService》》》定义一个后台托管服务///<summary>///自定义后台任务类///</summary>publicclassMyHostedService:BackgroundService{///<summary>///执行任务///</summary......
  • .net core微服务 async task job示例
     Execute函数publicasyncTaskExecute(IJobExecutionContextcontext){Stopwatch_time=newStopwatch();_time.Start();stringstrRe="开始时间:"+DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss");stringerrmsg=strin......
  • NetCore 3.1 大文件上传报错--413 Payload Too Large
    十年河东,十年河西,莫欺少年穷学无止境。,精益求精1、报错信息如下:RequestURL:https://xx.com/webapi/api/ad_video/UploadRequestMethod:POSTStatusCode:413PayloadTooLargeRemoteAddress:106.15.105.219:443ReferrerPolicy:strict-origin-when-cross-origin......