当前位置: 首页 > 编程笔记 >

C#中Task.Yield的用途深入讲解

太叔睿
2023-03-14
本文向大家介绍C#中Task.Yield的用途深入讲解,包括了C#中Task.Yield的用途深入讲解的使用技巧和注意事项,需要的朋友参考一下

前言

最近在阅读 .NET Threadpool starvation, and how queuing makes it worse 这篇博文时发现文中代码中的一种 Task 用法之前从未见过,在网上看了一些资料后也是云里雾里不知其解,很是困扰。今天在程序员节的大好日子里终于想通了,于是写下这篇随笔分享给大家,也过过专心写博客的瘾。

这种从未见过的用法就是下面代码中的 await Task.Yield() :

static async Task Process()
{
 await Task.Yield();

 var tcs = new TaskCompletionSource<bool>();

 Task.Run(() =>
 {
  Thread.Sleep(1000);
  tcs.SetResult(true);
 });

 tcs.Task.Wait();
}

注:上面的代码不是示例,只是因为这段代码而初遇 await Task.Yield)

Task.Yield 简单来说就是创建时就已经完成的 Task ,或者说执行时间为0的 Task ,或者说是空任务,也就是在创建时就将 Task 的 IsCompeted 值设置为0。

那 await 一个空任务会怎样?我们知道在 await 时会释放当前线程,等所 await 的 Task 完成时会从线程池中申请新的线程继续执行 await 之后的代码,这本来是为了解决异步操作(比如IO操作)霸占线程实际却用不到线程的问题,而 Task.Yield 却产生了一个不仅没有异步操作而且什么也不干的 Task ,不是吃饱了撑着吗?

今天吃晚饭的时候终于想明白了——吃饱了没有撑。Task.Yield 产生的空任务仅仅是为 await 做嫁衣,而真正的图谋是借助 await 实现线程的切换,让 await 之后的操作重新排队从线程池中申请线程继续执行。

这样做有什么好处呢?

线程是非常非常宝贵的资源,千金难买一线程,而且有优先级,提高线程利用率的重要手段之一就是及时将线程分配给最需要的地方,而最奢侈的之一是让一个优先级低执行时间长的操作一直占用着一个线程,await Task.Yield 可以让你巧妙地借助 await 的线程切换能力,将不太重要的比较耗时的操作放在新的线程(重新排队从线程池中申请到的线程)中执行。打个比方,很多人排队在外婆家就餐,你来的时候比较巧,正好有位置,但你本来就不着急肚子也不太饿准备慢慢吃慢慢聊,而排队的人当中有些人很饿很着急吃完还有事,这时你如果先点几个招牌菜解解馋,然后将座位让出来,重新排队,并且排队的人当中像你这样的都这么做,那些排队中心急如焚的人真是是幸福感爆棚,外婆家的老板也笑弯了腰。你让出座位重新排队的爱心行为就是 await Task.Yield() 。

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对小牛知识库的支持。

 类似资料:
  • 本文向大家介绍深入讲解C++中的构造函数,包括了深入讲解C++中的构造函数的使用技巧和注意事项,需要的朋友参考一下 C++构造函数 当创建一个对象时,往往需要做一些初始化工作,例如对数据成员赋值等。为了解决这个问题,C++提供了构造函数。 构造函数(Constructor)是一种特殊的成员函数,它的名字和类名相同,没有返回值,不需要用户调用(用户也不能调用),而是在创建对象时自动执行。构造函数的作

  • 本文向大家介绍C#中的协变与逆变深入讲解,包括了C#中的协变与逆变深入讲解的使用技巧和注意事项,需要的朋友参考一下 什么是协变与逆变 MSDN的解释: https://msdn.microsoft.com/zh-cn/library/dd799517.aspx 协变和逆变都是术语,前者指能够使用比原始指定的派生类型的派生程度更小(不太具体的)的类型,后者指能够使用比原始指定的派生类型的派生程度更大

  • 本文向大家介绍Python中作用域的深入讲解,包括了Python中作用域的深入讲解的使用技巧和注意事项,需要的朋友参考一下 前言 作用域是指变量的生效范围,例如本地变量、全局变量描述的就是不同的生效范围。 python的变量作用域的规则非常简单,可以说是所有语言中最直观、最容易理解的作用域。 在开始介绍作用域之前,先抛一个问题: 上面的代码将输出3、1、1。解释参见再述作用域规则。另外,个人建议,

  • 本文向大家介绍MyBatis中$和#的深入讲解,包括了MyBatis中$和#的深入讲解的使用技巧和注意事项,需要的朋友参考一下 这是一次代码优化过程中发现的问题,在功能优化后发现部分数据查不到出来了,问题就在于一条sql上的#和$。 下图为两条sql: 从图上可以看出 wwlr.LabelId in(${showLabels}) 和 wwlr.LabelId in(#{showLabels}),其

  • 本文向大家介绍Spring中@Autowire注入的深入讲解,包括了Spring中@Autowire注入的深入讲解的使用技巧和注意事项,需要的朋友参考一下 一直在思考spring的@Autowire注入属性时到底是按类型注入还是按名称注入,今天写了一个测试来证明一下。 定义接口TestService 定义接口实现:TestServiceImpl1和TestServiceImpl2 定义一个bean

  • 本文向大家介绍C#装箱与拆箱操作的深入讲解,包括了C#装箱与拆箱操作的深入讲解的使用技巧和注意事项,需要的朋友参考一下 疑问 都知道C#有装箱和拆箱的操作,听闻也都是讲int类型转换成object类型就是装箱,将object类型再转回int类型就是拆箱。 描述的通俗点: 装箱 将值类型转换成引用类型, 拆箱 将引用类型转换成值类型。 那看来是要先了解一下引用类型和值类型了。 引用类型和值类型 在C

  • 本文向大家介绍Tomcat中的Session与Cookie深入讲解,包括了Tomcat中的Session与Cookie深入讲解的使用技巧和注意事项,需要的朋友参考一下 前言 HTTP 是一种无状态通信协议,每个请求之间相互独立,服务器不能识别曾经来过的请求。而对于 Web 应用,它的活动都是依赖某个状态的,比如用户登录,此时使用 HTTP 就需要它在一次登录请求后,有为后续请求提供已登录信息的能力

  • 本文向大家介绍深入讲解iOS开发中的UIViewController,包括了深入讲解iOS开发中的UIViewController的使用技巧和注意事项,需要的朋友参考一下 UIViewController顾名思义:视图控制器。应该在MVC设计模式中扮演控制层的角色。一些初学者在最开始的时候一直不理解为何有了UIView还要UIViewController做什么用,不都是向视图中增加view。在此我