zoukankan      html  css  js  c++  java
  • await Task.Yield()和await Task.CompletedTask有什么不同

    有时候我们在代码中要执行一些非常耗时的操作,我们不希望这些操作阻塞调用线程(主线程)的执行,因为调用线程(主线程)可能还有更重要的工作要做,我们希望将这些非常耗时的操作由另外一个线程去执行,这个时候就可以用到await Task.Yield(),它借助了C# 5.0中的异步函数关键字await async,将await关键字之后的代码交由线程池中的另一个线程执行(前提是项目的SynchronizationContext.Current为null)。

    那么有同学可能会纳闷,await Task.Yield()await Task.CompletedTask有什么不同吗?

    它俩可大不一样

    • Task.CompletedTask本质上来说是返回一个已经完成的Task对象,所以这时如果我们用await关键字去等待Task.CompletedTask,.NET Core认为没有必要再去线程池启动一个新的线程来执行await关键字之后的代码,所以实际上await Task.CompletedTask之前和之后的代码是在同一个线程上同步执行的,通俗易懂的说就是单线程的。这也是为什么很多文章说,使用了await async关键字并不代表程序就变成异步多线程的了。
    • Task.Yield()就不一样了,我们可以理解Task.Yield()是真正使用Task来启动了一个线程,只不过这个线程什么都没有干,相当于在使用await Task.Yield()的时候,确实是在用await等待一个还没有完成的Task对象,所以这时调用线程(主线程)就会立即返回去做其它事情了,当调用线程(主线程)返回后,await等待的Task对象就立即变为完成了,这时await关键字之后的代码由另外一个线程池线程来执行。

    下面我用.NET Core控制台项目,写一个示例代码来演示await Task.Yield()await Task.CompletedTask的不同:

    using System;
    using System.Runtime.CompilerServices;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace NetCoreTaskYield
    {
        class Program
        {
            /// <summary>
            /// TaskYield使用await Task.Yield(),是真正的异步执行,await关键字之前和之后的代码使用不同的线程执行
            /// </summary>
            static async Task TaskYield()
            {
                Console.WriteLine("TaskYield before await, current thread id: {0}", Thread.CurrentThread.ManagedThreadId.ToString());
    
                await Task.Yield();//执行到await Task.Yield()时,调用TaskYield()方法的线程(主线程)立即就返回了,await关键字后面的代码实际上是由另一个线程池线程执行的
                //注意Task.Yield()方法返回的不是Task类的对象,而是System.Runtime.CompilerServices.YieldAwaitable结构体的实例
    
                Console.WriteLine("TaskYield after await, current thread id: {0}", Thread.CurrentThread.ManagedThreadId.ToString());
    
                Thread.Sleep(3000);//阻塞线程3秒钟,模拟耗时的操作
    
                Console.WriteLine("TaskYield finished!");
            }
    
            /// <summary>
            /// 模拟TaskYield的异步执行
            /// </summary>
            static Task TaskYieldSimulation()
            {
                //模拟TaskYield()方法中,await关键字之前的代码,由调用TaskYieldSimulation()方法的线程(主线程)执行
                Console.WriteLine("TaskYieldSimulation before await, current thread id: {0}", Thread.CurrentThread.ManagedThreadId.ToString());
    
                return Task.Run(() =>
                {
                    //使用Task.Run启动一个新的线程什么都不做,立即完成,相当于就是Task.Yield()
                }).ContinueWith(t =>
                {
                    //下面模拟的是TaskYield()方法中,await关键字之后的代码,由另一个线程池线程执行
    
                    Console.WriteLine("TaskYieldSimulation after await, current thread id: {0}", Thread.CurrentThread.ManagedThreadId.ToString());
    
                    Thread.Sleep(3000);//阻塞线程3秒钟,模拟耗时的操作
    
                    Console.WriteLine("TaskYieldSimulation finished!");
                });
            }
    
            /// <summary>
            /// TaskCompleted使用await Task.CompletedTask,是假的异步执行,实际上是同步执行,await关键字之前和之后的代码使用相同的线程执行
            /// </summary>
            static async Task TaskCompleted()
            {
                Console.WriteLine("TaskCompleted before await, current thread id: {0}", Thread.CurrentThread.ManagedThreadId.ToString());
    
                await Task.CompletedTask;//执行到await Task.CompletedTask时,由于await的Task.CompletedTask已经处于完成状态,所以.NET Core判定await关键字后面的代码还是由调用TaskCompleted()方法的线程(主线程)来执行,所以实际上整个TaskCompleted()方法是单线程同步执行的
    
                Console.WriteLine("TaskCompleted after await, current thread id: {0}", Thread.CurrentThread.ManagedThreadId.ToString());
    
                Thread.Sleep(3000);//阻塞线程3秒钟,模拟耗时的操作
    
                Console.WriteLine("TaskCompleted finished!");
            }
    
            static void Main(string[] args)
            {
                Console.WriteLine("Main thread id: {0}", Thread.CurrentThread.ManagedThreadId.ToString());
    
                Console.WriteLine("==============================================");
    
                TaskYield().Wait();
    
                Console.WriteLine("==============================================");
    
                TaskCompleted().Wait();
    
                Console.WriteLine("==============================================");
    
                TaskYieldSimulation().Wait();
    
                Console.WriteLine("Press any key to end...");
                Console.ReadKey();
            }
        }
    }

    执行结果如下所示:

    注意TaskYield()方法是真正的异步执行,TaskYieldSimulation()方法模拟演示了await Task.Yield()异步执行的原理,而TaskCompleted()方法是假的异步执行,实则为同步单线程执行。

    参考文献:

    终于明白了 C# 中 Task.Yield 的用途

    await Task.Yield(); 超简单理解!

    Does await Task.CompletedTask mean the async method will run synchronously? 

  • 相关阅读:
    深圳移动 神州行(大众卡/轻松卡/幸福卡)套餐资费(含香港日套餐)信息及使用方法
    PHP设置时区,记录日志文件的方法
    微信公众平台消息接口使用指南
    C#日期时间格式化
    使用CMD实现批量重命名[转]
    Python高效编程技巧
    实用WordPress后台MySQL操作命令
    ubuntu-wine
    Javascript 笔记与总结(2-8)对象2
    Swift5.3 语言指南(十) 枚举
  • 原文地址:https://www.cnblogs.com/OpenCoder/p/12201446.html
Copyright © 2011-2022 走看看