C#中关于Task.Yeild()的探究
在与同事讨论async/await内部实现的时候,突然想到Task.Yeild()这个函数,为什么呢,了解一点C#async/await内部机制的都知道,在await一个异步任务(函数)的时候,它会先判断该Task是否已经完成,如果已经完成,则继续执行下去,不会返回到调用方,原因是尽量避免线程切换,因为await后面部分的代码很可能是另一个不同的线程执行,而Task.Yeild()则可以强制回到调用方,或者说主动让出执行权,给其他Task执行的机会,可以把Task理解为协程,Task.Yeild()和Thread.sleep(0)有点相同。
为了证明我的结论成立,请看代码:
public static async Task Test1()
{
await Task.CompletedTask;
Thread.Sleep();
Console.WriteLine("Test1任务完成");
}
public static async Task Test2()
{
await Task.Delay();
Thread.Sleep();
Console.WriteLine("Test2任务完成");
}
public static async Task Test3()
{
await Task.Yield();
Thread.Sleep();
Console.WriteLine("Test3任务完成");
}
static void Main(string[] args)
{
Console.WriteLine(DateTime.Now);
_ = Test1();
Console.WriteLine(DateTime.Now);
Console.ReadLine();
}
按照开头的理论,Test1()异步函数由于await了一个已经完成的任务,所以会继续往下执行,阻塞1秒钟,然后回到调用方,打印的时间之差会相隔一秒。

Test2()异步函数由于await了一个未完成的任务(1ms对于CPU来说是很长的了),所以会返回调用方,然后打印相同的时间,一秒钟之后会打印执行完毕。

Test3()调用了Task.Yeild()函数,主动让出执行权,所以会直接返回调用方,然后打印相同的时间,一秒之后会打印执行完毕。

可以看到,开头的结论是正确的。那么,有什么意义呢?Yeild的意思在这里其实就是退让,让出的意思,让出什么呢?就是让出执行权,这与Thread.sleep(0)让出CPU执行权给其他线程(前提是有其他线程竞争)有机会执行是一个道理。
请看我的例子:
public static async Task OP1()
{
while (true)
{
await Task.Yield();//这里会捕捉同步上下文,由于是控制台程序,没有同步上下文,所以默认的线程池任务调度器变成同步上下文
//也就是说后面的代码将会在线程池上执行,由于线程池工作线程数量设置为1,所以必须主动让出执行权,让其他的
//任务有执行的机会
Console.WriteLine("OP1在执行");
Thread.Sleep();//模拟一些需要占用CPU的操作
}
}
public static async Task OP2()
{
while (true)
{
await Task.Yield();
Console.WriteLine("OP2在执行");
Thread.Sleep();
}
}
static async Task Main(string[] args)
{
ThreadPool.SetMinThreads(, );
ThreadPool.SetMaxThreads(, );
//Task.Run()方法默认使用线程池任务调度器执行任务,由于主线程不是线程池线程,所以使用Task.Run()
var t = Task.Run(async () =>
{
var t1 = OP1();
var t2 = OP2();
await Task.WhenAll(t1, t2);
});
await t;
Console.ReadLine();
}

可以看出OP1()和OP2()两个协程(Task)互相争用一个线程(用户模式下的CPU),如果不主动让出执行权,另一个协程(Task)将不会有机会执行。
例如:
public static async Task OP2()
{
while (true)
{
await Task.CompletedTask;//或者是直接去掉
Console.WriteLine($"OP2在执行 {DateTime.Now}");
Thread.Sleep();
}
}
这样OP1()将永远不会有机会执行。

C#中关于Task.Yeild()的探究的更多相关文章
- (转).NET 4.5中使用Task.Run和Parallel.For()实现的C# Winform多线程任务及跨线程更新UI控件综合实例
http://2sharings.com/2014/net-4-5-task-run-parallel-for-winform-cross-multiple-threads-update-ui-dem ...
- Ext.Net中的Task控件的使用
在用到Ext.Net中的Task控件的时候,写了一下基本的使用方法: 控件是在TaskManager里面的Tasks下面的Task 此控件的常用属性有,TaskID.Interval(设置间隔时间). ...
- SSIS中Sql Task 获取系统变量
原文:SSIS中Sql Task 获取系统变量 执行 SQL 任务使用不同的连接类型时,SQL 命令的语法使用不同的参数标记.例如,ADO.NET 连接管理器类型要求 SQL 命令使用格式为 @var ...
- Linux中的task,process, thread 简介
本文的主要目的是介绍在Linux内核中,task,process, thread这3个名字之间的区别和联系.并且和WINDOWS中的相应观念进行比较.如果你已经很清楚了,那么就不用往下看了. LINU ...
- Proxmox VE中出现TASK ERROR: command 'apt-get update' failed: exit code 100的解决方法
问题描述: 出现这个错误一般在WEB或者在Proxmox VE的服务器上面能看到日志: PVE中出现TASK ERROR: command 'apt-get update' failed: exit ...
- Android中的task和stack
今天在重新理了一遍intent的过程中发现task是一个神奇的东西,而它又和stack有着很深的联系.task顾名思义是一个任务,但是这个任务可不一定只是来自一个app,比如我用微信来发一张图片,那么 ...
- SV中的task和function
SV中class的properties和methods默认都是public的,但是可以声明为local和protected. 一个properties声明为local类型的,则只在该class中的me ...
- Java并发包中线程池ThreadPoolExecutor原理探究
一.线程池简介 线程池的使用主要是解决两个问题:①当执行大量异步任务的时候线程池能够提供更好的性能,在不使用线程池时候,每当需要执行异步任务的时候直接new一个线程来运行的话,线程的创建和销毁都是需要 ...
- swoole中使用task进程异步的处理耗时任务
我们知道,swoole中有两大进程,分别是 master 主进程和 manager 管理进程. 其中 master 主进程中会有一个主 reactor 线程和多个 reactor 线程,主要的作用就是 ...
随机推荐
- vue无法选择上传相同文件
使用h5自带的input type=file时,使用change触发上传事件 <input class="exportss" type="file" id ...
- 朋友,您可能是MCR的受害者
2018 年五月之后,微软将后续发布的所有 docker image 都推送到了 MCR (Miscrosoft Container Registry),但在中国大陆,它的速度实在是令人发指,本文将介 ...
- Python使用wxpy模块实现微信两两群组消息同步
python使用wxpy模块提供的微信API接口实现两两群组的消息同步 安装模块: pip install wxpy 注意:需要同步的微信群需要保存到通讯录中 以下是自己闲来无事写的代码,暂时还存在以 ...
- FastReport分组与聚合
本来看上去都觉得简单顺便训练下,是想对Customer表中的Company字段以第1个开头的字母分组,结果自己因喜欢将那些东西都集中在一起进行训练,在那个Master-Slave上做例子,并且没用另外 ...
- 透过源码看懂Flink核心框架的执行流程
前言 Flink是大数据处理领域最近很火的一个开源的分布式.高性能的流式处理框架,其对数据的处理可以达到毫秒级别.本文以一个来自官网的WordCount例子为引,全面阐述flink的核心架构及执行流程 ...
- 使用ansible实现批量免密认证
一.目的 批量实现免密认证,适合管理大批量机器使用 二.步骤 1-1.第一种方式:收集被控制主机的公钥,用于构建并验证ssh_known_hosts # ssh-keyscan 10.246.151. ...
- Android学习笔记显示和隐藏ActionBar
要在应用中使用ActionBar 需要android:minSdkVersion:11以上,现在基本都可以用了 创建Android项目如果不做特殊设置默认都带有ActionBar 如果不想要Actio ...
- READSJC.md
这个作业属于哪个课程 软件工程 这个作业要求在哪里 点我 这个作业的目标 介绍自己 作业正文 往下看啦 其他参考文献 空空如也 介绍自己: 我是综合实验班的孙劼成. 天天宅在家里实在是太无聊了,就背背 ...
- MongoDB设计方法及技巧
MongoDB是一种流行的数据库,可以在不受任何表格schema模式的约束下工作.数据以类似JSON的格式存储,并且可以包含不同类型的数据结构.例如,在同一集合collection 中,我们可以拥有以 ...
- C# 9.0 新特性之只读属性和记录
阅读本文大概需要 2 分钟. 大家好,这是 C# 9.0 新特性系列的第 4 篇文章. 熟悉函数式编程的童鞋一定对"只读"这个词不陌生.为了保证代码块自身的"纯洁&quo ...