.NET(C#):await返回Task的async方法

最新推荐文章于 2025-06-12 13:05:34 发布
王晓鹏 最新推荐文章于 2025-06-12 13:05:34 发布
阅读量2.6k 收藏
点赞数
分类专栏: C#/WPF/.NET
C#/WPF/.NET 专栏收录该内容
43 篇文章
订阅专栏
本文探讨了C#中async方法的不同返回类型及其对await操作的影响。详细分析了返回Task和Task<T>类型的async方法如何处理内部await操作,并展示了这些方法在实际编程中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

.NET(C#):await返回Task的async方法


众所周知,async方法只可以返回void,Task和Task<T>。


 


对于返回void的async方法,它并不是awaitable,所以其他方法不能用await方法来调用它,而返回Task的async方法则可以。


 


那么当async方法返回Task后,接着await,那被await的Task是一个什么概念?是async方法中第一个被await的Task?不,它代表目标async方法的全部执行,其中包括被await分割的连接Task,但是不包括非await造成的多线程执行。


 


如下代码,在doo是一个返回Task的async方法,然后在另一个方法test中await调用doo,然后在Main方法中调用test(由于Main方法不允许加async,所以需要另外加一个async方法来使用await)


static void Main(string[] args)
{
    test();
    log("Main:调用test后");
    Thread.Sleep(Timeout.Infinite);
}
 
//Main方法不允许加async,所以我们用这个方法使用await
static async void test()
{
    log("test: await之前");
    await doo();
    log("test: await之后");
}
 
//返回Task的async方法
static async Task doo()
{
    log("doo: Task结果:" + await Task.Run(() => { Thread.Sleep(1000); log("Task"); return 1; }));
    log("doo: Task结果:" + await Task.Run(() => { Thread.Sleep(1000); log("Task"); return 2; }));
    log("doo: Task结果:" + await Task.Run(() => { Thread.Sleep(1000); log("Task"); return 3; }));
    Thread.Sleep(1000);
    Console.WriteLine("doo中在Task外的Thread.Sleep执行完毕");
}
 
//输出方法:显示当前线程的ManagedThreadId
static void log(string msg)
{
    Console.WriteLine("{0}: {1}", Thread.CurrentThread.ManagedThreadId, msg);
}
 


上面代码会输出:


1: test: await之前
1: Main:调用test后
3: Task
3: doo: Task结果:1
4: Task
4: doo: Task结果:2
3: Task
3: doo: Task结果:3
doo中在Task外的Thread.Sleep执行完毕
3: test: await之后
 


前两句简单,调用test方法,await后的内容会被加在目标Task的后面,然后test马上返回,于是输出“Main:调用test后”,同时他们都是在主线程中执行的,所以ManagedThreadId都是1。


 


接着后面就是另一个Task的执行(当然在另一个线程,也是test方法中await的目标Task)。这个所谓的Task就是doo方法的全部执行。所以doo中三个顺序执行的Task(通过await一个一个连接)依次执行,所以Task输出结果1,2,3。第一个Task的ManagedThreadId是3,第二个是4,第三个又是3,原因是Task的内部执行使用了CLR的线程池,所以线程得到了重复利用。


 


接着doo方法还没有完,最后一个await造成doo方法后面的代码在这个await针对的Task执行后继续执行,于是输出:doo中Task外的Thread.Sleep执行完毕。


 


最后当doo彻底执行完test的await才结束,所以最后一行输出:test:await之后。


 


 


上面我说过:被await的async方法返回的Task代表“目标async方法的全部执行,其中包括被await分割的连接Task,但是不包括非await造成的多线程执行”。


所以如果把返回Task的async方法(也就是上例中的doo方法)改成这样:


//返回Task的async方法
static async Task doo()
{
    log("doo: Task结果:" + await Task.Run(() => { Thread.Sleep(1000); log("Task"); return 1; }));
    log("doo: Task结果:" + await Task.Run(() => { Thread.Sleep(1000); log("Task"); return 2; }));
    log("doo: Task结果:" + await Task.Run(() => { Thread.Sleep(1000); log("Task"); return 3; }));
 
    //不使用await:线程池多线程
    ThreadPool.QueueUserWorkItem(_ =>
        {
            Thread.Sleep(1000);
            Console.WriteLine("ThreadPool.QueueUserWorkItem");
        });
 
    //不使用await:Task多线程
    Task.Run(() =>
        {
            Thread.Sleep(1000);
            Console.WriteLine("Task.Run");
        });
}
 


我们加入了不用await的多线程执行,分别使用ThreadPool和Task,整个程序会输出这样的结果:


1: test: await之前
1: Main:调用test后
3: Task
3: doo: Task结果:1
4: Task
4: doo: Task结果:2
3: Task
3: doo: Task结果:3
3: test: await之后
Task.Run
ThreadPool.QueueUserWorkItem
 


不使用await的多线程完全脱离了test方法中await的Task,是运行在test的await之后的。


 


另外Visual Studio会对Task.Run代码做如下警告:






提示:Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the ‘await’ operator to the result of the call.


就是说,如果不加await,当前方法会继续执行直到结束,不用管他,因为我们现在就是在做在async方法中不用await的测试,呵呵。


 


 


或许你会问,为什么要用这样的方式去await另一个async方法返回的Task呢?我们一直在讨论返回Task的async方法,我认为看一个返回Task<T>的async方法可以更好地解释这个问题。


 


下面我们把上面的代码改成相似的返回Task<int>的async方法执行,那么doo方法返回Task<T>,他把自己方法内3个awaited Task的结果统一相加,最后返回结果并作为自己返回的Task的结果。然后在test方法中输出doo返回的结果。


 


完整代码:


static void Main(string[] args)
{
    test();
    log("Main:调用test后");
    Thread.Sleep(Timeout.Infinite);
}
 
//Main方法不允许加async,所以我们用这个方法使用await
static async void test()
{
    log("test: await之前");
    Console.WriteLine("doo结果:{0}", await doo());
    log("test: await之后");
}
 
//返回Task的async方法
static async Task<int> doo()
{
    var res1 = await Task.Run(() => { Thread.Sleep(1000); log("awaited Task1执行"); return
    var res2 = await Task.Run(() => { Thread.Sleep(1000); log("awaited Task2执行"); return
    var res3 = await Task.Run(() => { Thread.Sleep(1000); log("awaited Task3执行"); return
 
    //不使用await:线程池多线程
    ThreadPool.QueueUserWorkItem(_ =>
        {
            Thread.Sleep(1000);
            Console.WriteLine("ThreadPool.QueueUserWorkItem");
        });
 
    //不使用await:Task多线程
    Task.Run(() =>
        {
            Thread.Sleep(1000);
            Console.WriteLine("Task.Run");
        });
 
    return res1 + res2 + res3;
}
 
//输出方法:显示当前线程的ManagedThreadId
static void log(string msg)
{
    Console.WriteLine("{0}: {1}", Thread.CurrentThread.ManagedThreadId, msg);
}
 


先看结果:


1: test: await之前
1: Main:调用test后
3: awaited Task1执行
4: awaited Task2执行
4: awaited Task3执行
doo结果:6
4: test: await之后
ThreadPool.QueueUserWorkItem
Task.Run
 


 


和上一个返回Task的例子一样,当在test方法中await doo方法返回的Task,doo内awaited Task都被先等了,而没有awaited的线程都并没有被等,这是为什么呢(也就是上面留下的那个问题)?下面用这个返回Task<int>的例子解释一下:


在test中await doo返回的Task,那么此时我们需要他的结果,而他的结果是需要自己方法内所包含的其他awaited结果,可以理解成被等的子结果。所以自己的结果需要其他的结果,那么等这个结果必须需要等那些被依赖的结果也出来。所以test方法await doo方法的结果会同样等待所有doo内的await,不会管其他doo内非await的多线程执行(当然从技术角度讲,也是不可能的,因为async/await可以这样全靠的是编译器)。


 


 

确定要放弃本次机会?
福利倒计时
: :

立减 ¥

普通VIP年卡可用
立即使用
王晓鹏
关注 关注
  • 0
    点赞
  • 踩
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
  • 分享
    复制链接
    分享到 QQ
    分享到新浪微博
    扫一扫
  • 举报
    举报
专栏目录
.NET/C# 多线程和异步-Task和async/await使用
Code365
07-10 1679
通过上边的介绍,我们知道async/await是基于Task的,而Task是对ThreadPool的封装改进,主要是为了更有效的控制线程池中的线程(ThreadPool中的线程,我们很难通过代码控制其执行顺序,任务延续和取消等等);task.WhenAll(Task[] tasks) 表示所有的task都执行完毕后再去执行后续的操作, task.WhenAny(Task[] tasks) 表示任一task执行完毕后就开始执行后续操作。上边的所有示例中Task的执行都是异步的,不会阻塞主线程。
C#,await,async,task
m0_73786910的博客
07-23 1410
这背后有一个复杂的任务调度和上下文恢复机制,dotnet是很复杂的,我作为C#初学者并没有能够具体搞懂其中的奥妙,但是我可以简单的理解为,定义了Task之后,可以由await进行调用,并且与之相关的一系列代码都会进入一个新的线程中完成,主线程会绕过与之相关的代码,去完成接下来的任务,至于async,很遗憾我也不能解释清楚,但是显然的是,使用了这一套方法的函数和普通函数相当不一样,内部有一套自己的处理逻辑,async更像是告诉编译器的一种声明("你对我要特殊对待"),这个会同步运行一个task。
参与评论 您还未登录,请先 登录 后发表或查看评论
C# async&await异步
时意
12-25 397
https://www.cnblogs.com/liqingwen/p/5831951.html参考文章 启动程序时,系统会在内存中创建一个新的进程。进程是构成运行程序资源的集合。 在进程内部,有称为线程的内核对象,它代表的是真正的执行程序。系统会在 Main 方法的第一行语句就开始线程的执行。 线程: ①默认情况,一个进程只包含一个线程,从程序的开始到...
如何在不等待的情况下安全地在C#中调用异步方法
xfxf996的博客
08-15 2881
I have an async method which returns no data: 我有一个不返回任何数据的async方法: public async Task MyAsyncMethod
C# TAP异步编程(Task/async/await)总结
最新发布
hao1183716597的专栏
06-12 536
C#中有个很好用的东西,TAP异步编程(Task-based Asynchronous Pattern),是目前C#推荐的异步编程模型。它基于 System.Threading.Tasks.Task 和 async/await 关键字,旨在简化异步代码的编写、调试和维护。TAP 是现代 .NET 应用开发中最常用、最推荐的异步编程方式。要实现TAP,有三个基本元素:Task、async和await。在Winform中,使用好TAP编程,非常优雅。
c# 第五课 async await
dicheng6608的博客
04-19 249
Async 方法有三种可能的返回类型: Task、Task<T> 和 void,但是 async 方法的固有返回类型只有 Task 和 Task<T>。 当从同步转换为异步代码时,任何返回类型 T 的方法都会成为返回 Task<T> 的 async 方法,任何返回 void 的方法都会成为返回 Task 的 async 方法。...
C#异步task任务 await与async的正确打开方式
Peter的博客
04-27 1728
C#5.0推出了新语法,await与async,但相信大家还是很少使用它们。关于await与async有很多文章讲解,但有没有这样一种感觉,你看完后,总感觉这东西很不错,但用的时候,总是想不起来,或者不知道该怎么用。 为什么呢?我觉得大家的await与async的打开方式不正确。 首先看下使用约束。 1、await 只能在标记了async的函数内使用。 2、await 等待的函数必须标记a...
C#学习笔记:await/async 语法糖衣
JIA的博客
03-23 1346
多线程视频 学习笔记,对await效果理解: 1、await 使在其之后的 语句运行 在await修饰的Task子线程中执行,await后面的语句有种被Task子线程 回调 的意思。不过这是在 控制台中测试的效果。 参考 cnblog申江渔夫 - await之后的线程问题的相关测试结论:由三次试验及相关资料可以得出结论,await之后的线程依据SynchronizationContext在不同...
C# 异步编程实战:巧用 async/await 提升 ASP.NET Core 应用响应速度
威哥说编程
12-04 1062
异步编程是一种在程序执行过程中,某些操作不阻塞线程的技术。传统的同步操作会导致线程在执行 I/O 密集型操作时(例如数据库查询、文件读写、HTTP 请求等)被阻塞,直到操作完成,而异步操作则允许线程在等待结果的过程中继续执行其他任务。在 C# 中,异步编程通常通过async和await关键字来实现。这两者结合使用,可以让你在执行异步操作时,保持代码的结构和同步代码一样简洁。通过在 ASP.NET Core 应用中巧妙地使用async和await。
c#知识总结-任务并行库(Task async await)
liutao0124的博客
07-06 1288
在c#中,‘async’和‘await’是用于异步编程的关键字,它们允许你编写异步、非阻塞的代码。异步编程是为了避免在执行耗时操作主线程,从而提高程序的响应性和性能。‘async’关键字用于声明一个方法时异步的,而‘await’关键字则用于只是编译器在等待异步操作完成时暂停方法的执行。使用‘await’关键字时,编译器会生成代码来等待异步操作的完成,并在操作完成后恢复方法的执行。一个简单的示例ReadLine();//使用TPL,不会阻塞主程序try。
C# 异步编程详解(Task,async/await)
luxingyu329的博客
09-10 3465
同步和异步主要用于修饰方法。当一个方法被调用时,调用者需要等待该方法执行完毕并返回才能继续执行,我们称这个方法是同步方法;当一个方法被调用时立即返回,并获取一个线程执行该方法内部的业务,调用者不用等待该方法执行完毕,我们称这个方法为异步方法。异步的好处在于非阻塞(调用线程不会暂停执行去等待子线程完成),因此我们把一些不需要立即使用结果、较耗时的任务设为异步执行,可以提高程序的运行效率。
c# 多线程(轮询,等待,回调)操作实例
07-02
c# 实现多线程的操作,轮询等待回调等异步操作,适合初学者的源码
在 C# 中使用 async、await、Task 进行异步编程
allway2的博客
02-26 3889
C# 和 .NET Framework(4.5 和核心)支持使用一些本机函数、类和保留关键字的异步编程。 在我们了解什么是异步编程之前,让我们通过下面的控制台示例来了解什么是同步编程。 示例:异步程序 Copy static void Main(string[] args) { LongProcess(); ShortProcess(); } static void LongProcess() { Console.WriteLine("Lo.
C# 异步编程(async和await)
weixin_52026996的博客
08-01 4739
在C#中,如果需要 I/O 绑定(例如从网络请求数据、访问数据库或读取和写入到文件系统),则需要利用异步编程。 还可以使用 CPU 绑定代码(例如执行成本高昂的计算),对编写异步代码而言,写法简单易用。异步编程其实也就是Task实现的多线程。本文主要介绍C# 异步编程(async和await)。...
C#如何使用Task完成异步方法
weixin_66598341的博客
04-06 5526
简单介绍了在C#中如何使用Task完成简单的异步方法,内容包含了创建简单的异步方法,异步的取消,异步的等待...
.Net 多线程 异步编程 Await、Async和Task
weixin_30510153的博客
01-16 798
await和async简介   await和async是在C#5中引入,并且在.NetFramewor4.5以及.NetCore中进行了支持。主要是解决性能瓶颈,并且增强系统的响应能力。 msdn关于await以及async运行机理的描述 以上demo详细的描述了使用await时程序的运行机制,需要注意的以下几点: 第五步:执行完成后如果发现下一步还有await即设置了中断点,此时命令...
【C#】44. Task<T> 根据带返回值的函数构造Task
热门推荐
huiwuhuiwu的专栏
12-11 3万+
上篇中,我们看了不带参数值和返回值的函数(Action)构造Task的方法,本篇中我们想Thread一样,看看如何给Task传参和返回值。 首先定义int TaskMethod(String)函数,该函数以String作为参数,返回Int类型结果: static int TaskMethod(string name) { Console.WriteLine("Task {0} 运行在线程id为
C# 用async和await表达异步
Leo-夜空的博客
12-20 433
async和await async: 声明异步方法 使用await,需要用async修饰await所在的方法 await:等待异步方法执行完毕但不会阻塞主线程 将async方法分为两部分,await前的为async方法执行的主线程,await之后的部分被安排为“后续操作”在子线程中执行 await不会创建子线程,只是通知并记录有一个异步方法的执行(Task.Run()创建一个子线程) st...
.Net 异步编程之async和await
Qanx的博客
03-19 2156
async、await原理,异步方法≠多线程。await 看似“等待”,但经过编译后,其实没有“wait”。await 调用“等待”期间,.Net 会把当前的线程返回给线程池,等异步方法调用执行完毕后,框架会从线程池再取出一个线程执行后续的代码。
王晓鹏

博客等级

码龄20年
175
原创
18
点赞
82
收藏
58
粉丝
关注
私信

热门文章

  • 视频文件里的480P、720P、1080P是什么意思? 14587
  • Win7 IIS7.5+PHP Manager安装配置PHP5+Mysql教程 9003
  • iPhone4S和iPad2的iOS5.0.1完美越狱 6695
  • 简单实现ListView SubItem重绘 5833
  • Get the WPF Window handle 4659

分类专栏

  • Android
    55篇
  • ios
    74篇
  • C++/DShow/Media
    54篇
  • C#/WPF/.NET
    43篇
  • JSP/PHP/SQL/NodeJS
    21篇
  • Git/Other
    10篇

展开全部 收起

上一篇:
C#==>匿名方法 匿名方法的【λ(拉姆达)表达式】方式定义
下一篇:
一个例子 理解 await

最新评论

  • editbox文本上下居中(垂直居中)的有效方法

    QQ_1348207385: VS2019单行无效果

  • Android 判断应用 第一次启动

    ZhiPingFit: versionCode跟是否第一次启动有关系吗? 如果app没有升级版本那不是每次读取都是一样

  • Android圆形图片--自定义控件

    小太阳1988@: 没有实现啊。不是圆形图片。问下大神是不是漏了什么diamanté

  • rtmp 使用

    视界音你而不同: 您好,请教一下,RTMP_SetBufferMS(rtmp, (uint32_t) (2 * 3600 * 1000));,这个函数您设置的缓存时间为2小时,有什么根据吗,他会影响到什么,可不可以分享一下,谢谢。

大家在看

  • 1款强到让大厂害怕的软件,请低调使用! 122
  • (LeetCode 每日一题) 1695. 删除子数组的最大得分 (哈希表+双指针)
  • 【IoTDB】363万点/秒写入!IoTDB凭何领跑工业时序数据库赛道? 497
  • C++:迭代器 64
  • msyql:MySQL长事务深度剖析

最新文章

  • 博客搬家至以下地址
  • APP紧急修改后的app store官方审核通道
  • Android TAb分页菜单实现总结
2015年67篇
2014年25篇
2013年44篇
2012年78篇
2011年58篇
2010年66篇
2009年41篇
2008年110篇
2007年16篇
2006年3篇

目录

展开全部

收起

目录

展开全部

收起

上一篇:
C#==>匿名方法 匿名方法的【λ(拉姆达)表达式】方式定义
下一篇:
一个例子 理解 await

分类专栏

  • Android
    55篇
  • ios
    74篇
  • C++/DShow/Media
    54篇
  • C#/WPF/.NET
    43篇
  • JSP/PHP/SQL/NodeJS
    21篇
  • Git/Other
    10篇

展开全部 收起

目录

评论
被折叠的  条评论 为什么被折叠? 到【灌水乐园】发言
查看更多评论
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值