在与同事讨论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. @luogu - P6109@ [Ynoi2009]rprmq

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 有一个 n×n 的矩阵 a,初始全是 0,有 m 次修改操作和 ...

  2. postman获得时间戳和md5加密的方法

    注意点:记得用postman.setGlobalVariable设置全局变量,不然{{strmd5}}这种变量取不到值

  3. ZWave对COMAND CLASS的处理流程

    文章主题  在开发一个 ZWave Device 的过程中,对 COMAND CLASS(单词太长了,后面就简写为 CC 啦) 的处理是最基本.最重要的工作.这篇文章以最最简单的 CC:COMMNAD ...

  4. Springboot打包放到Tomcat中报错 One or more listener fail to start

    1.问题: Springboot项目直接启动不报错,打war包放到外部容器Tomcat.东方通上,在@Weblistener注解的监听器类中报错 One or more listener fail t ...

  5. UltraEdit常用技巧

    Tip 1: Alt+C 列模式可以说最初选择使用这个文本编辑软件,原因很简单,就是因为“她”具有列编辑模式.如果您还不知道什么是列编辑模式的话,我想您应该好好研究一下啦.这是一个超级“赞”的功能.在 ...

  6. Redis安装过程jemalloc/jemalloc.h报错

    问题: [root@localhost redis-3.0.0]# make cd src && make all make[1]: Entering directory `/data ...

  7. 人脸识别Demo解析C#

    概述 不管你注意到没有,人脸识别已经走进了生活的角角落落,钉钉已经支持人脸打卡,火车站实名认证已经增加了人脸自助验证通道,更别提各个城市建设的『智能城市』和智慧大脑了.在人脸识别业界,通常由人脸识别提 ...

  8. java之FTP上传下载

    import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import ...

  9. vue element安装

    element-ui插件 安装 >: cnpm i element-ui -S main.js配置 import ElementUI from 'element-ui'; import 'ele ...

  10. 前端开发神器Charles从入门到卸载

    前言 本文将带大家学习使用前端开发神器-charles,从基本的下载安装到常见配置使用,为大家一一讲解. 一.花式夸奖Charles 截取 Http 和 Https 网络封包. 支持重发网络请求,方便 ...