时间:2021-05-20
在与同事讨论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()的资料请关注其它相关文章!
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
C#异步方法返回void和Task的区别如果异步(async关键字)方法有返回值,返回类型为T时,返回类型必然是Task。但是如果没有返回值,异步方法的返回类型
本节讨论下在C++11中怎样使用std::async来执行异步task。C++11中引入了std::async什么是std::asyncstd::async()
C#代码与javaScript函数的相互调用问:1.如何在JavaScript访问C#函数?2.如何在JavaScript访问C#变量?3.如何在C#中访问Ja
在C++和C#中,命名空间用于尽可能地减少名称冲突。例如,在.NETFramework中,命名空间有助于将Microsoft.Build.Task.Messag
1.JavaScript访问C#函数2.JavaScript访问C#变量3.C#中访问JavaScript的已有变量4.如何在C#中访问JavaScript函数