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

C#关于Task.Yeild()函数的讨论

司徒英卓
2023-03-14
本文向大家介绍C#关于Task.Yeild()函数的讨论,包括了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(1000);
   Console.WriteLine("Test1任务完成");
}
public static async Task Test2()
{
   await Task.Delay(1);
   Thread.Sleep(1000);
   Console.WriteLine("Test2任务完成");
}
public static async Task Test3()
{
   await Task.Yield();
   Thread.Sleep(1000);
   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(1000);//模拟一些需要占用CPU的操作
   }
}
public static async Task OP2()
{
   while (true)
   {
     await Task.Yield();
     Console.WriteLine("OP2在执行");
     Thread.Sleep(1000);
   }
}
static async Task Main(string[] args)
{
   ThreadPool.SetMinThreads(1, 1);
   ThreadPool.SetMaxThreads(1, 1);
   //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(1000);
   }
}

      这样OP1()将永远不会有机会执行。

以上就是C#中关于Task.Yeild()函数的讨论的详细内容,更多关于C# Task.Yeild()的资料请关注小牛知识库其它相关文章!

 类似资料:
  • 本文向大家介绍关于C#中排序函数的总结,包括了关于C#中排序函数的总结的使用技巧和注意事项,需要的朋友参考一下 sort 函数对数组中的数据进行升序排序,(其中,sort函数有很多重载的形式,这里不再一一的说明) Reverse函数对数组中的数据进行降序排序, 如何把二个数组联系在一起进行排序操作呢? 例,在学生的信息中有学号和姓名,按学号输出学生的信息怎样实现??? 以上这篇关于C#中排序函数的

  • C ++是由Bjarne Stroustrup于1979年在贝尔实验室开始开发的一种中级编程语言。 C ++可在各种平台上运行,例如Windows,Mac OS和各种版本的UNIX。 本教程采用简单实用的方法来描述C ++的概念。

  • C是一种通用的,程序性的,命令式的计算机编程语言,由贝尔电话实验室的Dennis M. Ritchie于1972年开发,用于开发Unix操作系统。 C是最广泛使用的计算机语言,它与Java编程语言一起在第一流程中保持波动,Java编程语言也同样受欢迎并且在现代软件程序员中最广泛使用。 C标准库是一组C内置函数,常量和头文件,如<stdio.h>,<stdlib.h>,<math.h>等。该库将作为

  • git commit你们一般都是怎么写的呢? 我通常用的比较多的是feat、modify、fix、chore这些。 feat一般是新增页面或者组件。 modify用的最多,只要是在原有文件做了修改,都算这个。 fix就是修复了一些bug或者问题,有的问题只是改个文案啥的。 chore一般就是改了脚手架插件配置等,写的比较简单,commit就叫优化配置文件啥的。 另外有时候改动非常小,比方说修改错别

  • 本文向大家介绍tan()函数,用于C ++中的复数,包括了tan()函数,用于C ++中的复数的使用技巧和注意事项,需要的朋友参考一下 在本文中,我们将讨论C ++ STL中复数的工作原理,语法和函数示例。 tan()用于复数是<complex>头文件下的函数。此函数用于查找与其关联的复数的切线。此功能是<cmath>头文件下的simple的复杂版本。 什么是复数? 复数是由实数和虚数组成的数字。