在与同事讨论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()的探究的更多相关文章

  1. (转).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 ...

  2. Ext.Net中的Task控件的使用

    在用到Ext.Net中的Task控件的时候,写了一下基本的使用方法: 控件是在TaskManager里面的Tasks下面的Task 此控件的常用属性有,TaskID.Interval(设置间隔时间). ...

  3. SSIS中Sql Task 获取系统变量

    原文:SSIS中Sql Task 获取系统变量 执行 SQL 任务使用不同的连接类型时,SQL 命令的语法使用不同的参数标记.例如,ADO.NET 连接管理器类型要求 SQL 命令使用格式为 @var ...

  4. Linux中的task,process, thread 简介

    本文的主要目的是介绍在Linux内核中,task,process, thread这3个名字之间的区别和联系.并且和WINDOWS中的相应观念进行比较.如果你已经很清楚了,那么就不用往下看了. LINU ...

  5. Proxmox VE中出现TASK ERROR: command 'apt-get update' failed: exit code 100的解决方法

    问题描述: 出现这个错误一般在WEB或者在Proxmox VE的服务器上面能看到日志: PVE中出现TASK ERROR: command 'apt-get update' failed: exit ...

  6. Android中的task和stack

    今天在重新理了一遍intent的过程中发现task是一个神奇的东西,而它又和stack有着很深的联系.task顾名思义是一个任务,但是这个任务可不一定只是来自一个app,比如我用微信来发一张图片,那么 ...

  7. SV中的task和function

    SV中class的properties和methods默认都是public的,但是可以声明为local和protected. 一个properties声明为local类型的,则只在该class中的me ...

  8. Java并发包中线程池ThreadPoolExecutor原理探究

    一.线程池简介 线程池的使用主要是解决两个问题:①当执行大量异步任务的时候线程池能够提供更好的性能,在不使用线程池时候,每当需要执行异步任务的时候直接new一个线程来运行的话,线程的创建和销毁都是需要 ...

  9. swoole中使用task进程异步的处理耗时任务

    我们知道,swoole中有两大进程,分别是 master 主进程和 manager 管理进程. 其中 master 主进程中会有一个主 reactor 线程和多个 reactor 线程,主要的作用就是 ...

随机推荐

  1. eclipse中testNG的两种安装方式

    今天给大家带来两种关于testNG中的安装方式:1.在线安装(本人亲测有效!!!)2.离线安装 一.在线安装testNG插件的步骤: 1.给大家提供一个testNG在线的安装的地址:http://dl ...

  2. java特性 JDK JRE JVM

    1简单性 2可移植性性(跨平台) 3面向对象 4高性能 5分布式 6动态性 7多线程 8安全性JDK:java开发工具 . JRE:JDK:java运行环境 . JVM:JDK:java虚拟机

  3. LR脚本信息函数-lr_user_data_point

    Loadrunner中lr_user_data_point.lr_user_data_point_instance两个函数可以用来记录一条自定义的Vuser运行数据,并将其输出到测试结果中,最后可以通 ...

  4. Docker数据管理与挂载管理

    介绍如何在 Docker 内部以及容器之间管理数据:在容器中管理数据主要有两种方式:数据卷(Volumes).挂载主机目录 (Bind mounts) 镜像来源 [root@docker01 ~]# ...

  5. gdb基本命令总结

    本文介绍使用gdb调试程序的常用命令.  主要内容: [简介] [举例] [其他] [简介]  ============= GDB是GNU开源组织发布的一个强大的UNIX下的程序调试工具.如果你是在 ...

  6. 8.实战交付一套dubbo微服务到k8s集群(1)之Zookeeper部署

    1.基础架构 主机名 角色 ip HDSS7-11.host.com K8S代理节点1,zk1 10.4.7.11 HDSS7-12.host.com K8S代理节点2,zk2 10.4.7.12 H ...

  7. vscode启动vue项目出错,给了管理员权限没用

    今天在安装vue环境测试项目的时候, 发现vscode调用终端异常,语句无法运行,百度上给的解决方法是给管理员权限 给了以后发现没用,怎么试都没用,然后想到了,重启大法,然后问题就完美解决了

  8. web安全中的session攻击

    运行着个简单的demo后,打开login.jsp,使用firebug或chrome会发现,即使没有登录,我们也会有一个JSESSIONID,这是由服务器端在会话开始是通过set-cookie来设置的匿 ...

  9. Skywalking的存储配置与调优

    https://blog.csdn.net/smooth00/article/details/96479544 作为一款APM和全链路监控平台,Skywalking算是挺出色的.Skywalking是 ...

  10. Flask01-HelloWorld

    # flask学习 参考:http://www.pythondoc.com/flask-mega-tutorial/ ## python3. 默认支持虚拟环境使用(用最简单的方法,解决问题) wget ...