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

news/2024/11/19 16:58:10/文章来源:https://www.cnblogs.com/carmen-019/p/18555115

本文主要分为三个部分:

1、语法格式

2、线程调度情况

3、编程注意事项

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

 

 

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

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

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

 

 

1、简单的语法格式

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

异步的语法格式如下:

 

private async Task<TResult> DoSomeStuffAsync(..)

{

     ..

    await ..

    ..

}

 

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

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 可见执行情况

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

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/836876.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微软Office 2021 24年11月授权版

概述 Microsoft Office LTSC 2021 专业增强版是微软公司推出的一款专为企业客户设计的办公软件套件。该版本于2024年11月进行了批量许可版更新推送,旨在为企业用户提供更加稳定、高效的办公体验。主要特点LOGO设计趋势强化:新版Office将棱角改为圆角风格,提高了企业的辨识度…

OpenStack制作镜像

Ubuntu镜像的制作采用的是IOS 安装 转qcow2 上传OpenStack使用。 1.环境介绍 主机:Ubuntu 22.04 工具:QEMU+KVM 镜像类型:Ubuntu 22.04 工具:VNC-Client、系统镜像ISO或者IMG格式等,这里使用:ubuntu-22.04.5-live-server-amd64.iso2. 环境准备 root@node3:~/t# cat /proc…

ReNamer Pro 7.5 中文绿色便携专业版-文件重命名工具

前言 我们日常生活和工作中所涉及的文件数量日益增多。无论是图片、音频、视频还是各种文档,这些文件在存储、管理和分享时,都需要有一个清晰、有序的文件命名规则。然而,手动重命名大量文件不仅耗时耗力,而且容易出错,这对于追求效率和准确性的现代生活来说显然是不现实的…

接口测试之fiddler

二、Fiddler 简介 fiddler 是 C# 开发免费web调试工具之一,记录所有客户端和服务端常见的 http 以及 https 请求,可监视设断点,甚至修改输入输出数据,它还包含了一个强大的基于事件脚本的子系统,并且能使用 .net 语言来拓展。 Fiddler也是一款专用的抓包工具,也是一个调试…

DHCP介绍与实现方法

简介:动态主机配置协议(Dynamic Host Configuration Protocol,缩写:DHCP)是 RFC 1541(已被 RFC 2131 取代)定义的标准协议,该协议允许服务器向客户端动态分配 IP 地址和配置信息。 工作原理: DHCP协议支持C/S(客户端/服务器)结构,主要分为两部分: 1、DHCP客户端:…

推荐一个好用的 REST API 测试工具 Apifox

大家好啊!今天给大家安利一个超级好用的 REST API 测试工具 —— Apifox。说实话,作为一个经常和 API 打交道的开发者,以前总是被各种 API 测试和管理的问题困扰。直到遇到了 Apifox,才发现原来 API 测试可以这么舒服! Apifox 是啥? 简单来说,Apifox 就是一个"一站…

Qwen2.5-0.5B-Instruct搭建

模型地址 https://huggingface.co/Qwen/Qwen2.5-0.5B-Instruct简介 通义千问新一代开源模型Qwen2.5,旗舰模型Qwen2.5-72B性能超越Llama 405B,再登全球开源大模型王座。Qwen2.5全系列涵盖多个尺寸的大语言模型、多模态模型、数学模型和代码模型,每个尺寸都有基础版本、指令跟…

Pod环境安装(Mac)

原文链接:https://blog.csdn.net/huwan12345/article/details/135088993 背景知识: 安装pods需要依赖 ruby 环境,而安装 ruby 需要借助能够管理不同版本的 ruby工具 rvm,安装 rvm 又需要借助工具 Homebrew,Homebrew是一款Mac OS平台下的软件包管理工具,拥有安装、卸载、更…

CI配置项,IT服务的关键要素

随着现今数字经济的不断发展,逐渐成熟的IT 基础设施已不再是简单的竞争优势,而已成为企业生存和发展的基石。然而,仅仅拥有强大的基础设施是不够的。为了保障 IT 服务的平稳运行和持续交付,企业还需要重点关注 IT 服务的核心构建模块——配置项(Configuration Item,CI)。…

类的组合、继承、模板类、标准库

任务2 GradeCalc.hpp1 #include <iostream>2 #include <vector>3 #include <string>4 #include <algorithm>5 #include <numeric>6 #include <iomanip>7 8 using std::vector;9 using std::string;10 using std::cin;11 using std::cout;1…

cmu15545笔记-查询优化(Query Optimization)

目录概述Heuristics / RulesCost-based SearchSingle relationMutiple relationGenertive / Bottom-UpTransformation / Top-DownNested sub-queriesDecomposing QueriesExpression/Queries RewritingStatistics 概述 数据库系统的执行流程:从优化器到磁盘所设计的步骤:查询优…

构建企业级数据分析 Agent:架构设计与实现

引言 数据分析 Agent 是现代企业数据栈中的重要组件,它能够自动化数据分析流程,提供智能化的数据洞察。1. 数据处理工具链设计 数据处理工具链是整个分析系统的基础设施,它决定了系统处理数据的能力和效率。一个优秀的工具链设计应该具备:良好的可扩展性:能够轻松添加新的数据…