有时候我们在代码中要执行一些非常耗时的操作,我们不希望这些操作阻塞调用线程(主线程)的执行,因为调用线程(主线程)可能还有更重要的工作要做,我们希望将这些非常耗时的操作由另外一个线程去执行,这个时候就可以用到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();//阻塞线程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();//阻塞线程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();//阻塞线程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?

await Task.Yield()和await Task.CompletedTask有什么不同的更多相关文章

  1. 15.3 Task Task.Yield和Task.Delay说明

    https://blog.csdn.net/hurrycxd/article/details/79827958 书上看到一个Task.Yield例子,Task.Yield方法创建一个立即返回的awai ...

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

    上面的代码类似于: Task.Run(() => { }).ContinueWith(t => Do(LoadData())); 意思就是: loadData 如果耗时较长那么上述代码会产 ...

  3. 【5min+】帮我排个队,谢谢。await Task.Yield()

    系列介绍 [五分钟的dotnet]是一个利用您的碎片化时间来学习和丰富.net知识的博文系列.它所包含了.net体系中可能会涉及到的方方面面,比如C#的小细节,AspnetCore,微服务中的.net ...

  4. C#中 Thread,Task,Async/Await,IAsyncResult 的那些事儿!

    说起异步,Thread,Task,async/await,IAsyncResult 这些东西肯定是绕不开的,今天就来依次聊聊他们 1.线程(Thread) 多线程的意义在于一个应用程序中,有多个执行部 ...

  5. 从Thread,ThreadPool,Task, 到async await 的基本使用方法解读

    记得很久以前的一个面试场景: 面试官:说说你对JavaScript闭包的理解吧? 我:嗯,平时都是前端工程师在写JS,我们一般只管写后端代码. 面试官:你是后端程序员啊,好吧,那问问你多线程编程的问题 ...

  6. C#多线程和异步(二)——Task和async/await详解

    一.什么是异步 同步和异步主要用于修饰方法.当一个方法被调用时,调用者需要等待该方法执行完毕并返回才能继续执行,我们称这个方法是同步方法:当一个方法被调用时立即返回,并获取一个线程执行该方法内部的业务 ...

  7. Thread,ThreadPool,Task, 到async await 的基本使用方法和理解

    很久以前的一个面试场景: 面试官:说说你对JavaScript闭包的理解吧? 我:嗯,平时都是前端工程师在写JS,我们一般只管写后端代码. 面试官:你是后端程序员啊,好吧,那问问你多线程编程的问题吧. ...

  8. C#中如果用await关键字来await一个为null的Task对象会抛出异常

    await & async模式是C#中一个很重要的特性,可以用来提高异步程序(多线程程序)的执行效率.但是如果尝试用await关键字来await一个为null的Task对象,会导致程序抛出Nu ...

  9. C#中 Thread,Task,Async/Await,IAsyncResult 的那些事儿![转载]

    说起异步,Thread,Task,async/await,IAsyncResult 这些东西肯定是绕不开的,今天就来依次聊聊他们 1.线程(Thread) 多线程的意义在于一个应用程序中,有多个执行部 ...

随机推荐

  1. p2p平台详细运营框架

    市场拓展部1.负责完成公司市场销售.市场拓展.费用控制等年度目标任务,并负责将目标责任制分解落实,确保各项工作目标得以实现.2.对营销政策.市场及同业营销动态等方面进行调研分析,及时调整营销策略和计划 ...

  2. [转]Spring 注解大全与详解

    Spring使用的注解大全和解释 注解 解释 @Controller 组合注解(组合了@Component注解),应用在MVC层(控制层),DispatcherServlet会自动扫描注解了此注解的类 ...

  3. js(四) 全选/全不选和反选

    思路:通过选择全选的选框的状态stuts 即true/false控制其他选框. 首先 我们要通过.checked方法获取选框(全选/全不选)的值. function all(){ var stuts= ...

  4. spring json 返回中文乱码

    如前台显示的json数据中的中文为???,则可尝试以下方法. 方法一(推荐):在@RequestMapping中添加  produces={"text/html;charset=UTF-8; ...

  5. Roslyn NameSyntax 的 ToString 和 ToFullString 的区别

    本文告诉大家经常使用的 NameSyntax 拿到值的 ToString 和 ToFullString 方法的区别 从代码可以看到 NameSyntax 的 ToString 和 ToFullStri ...

  6. H3C IPv6地址构成

  7. Linux 内核class_simple 接口

    class_simple 接口意图是易于使用, 以至于没人会抱怨没有暴露至少一个包含设备的被 分配的号的属性. 使用这个接口只不过是一对函数调用, 没有通常的和 Linux 设备模型 关联的样板. 第 ...

  8. 2018-2-13-Xamarin-Forms-进度条控件

    title author date CreateTime categories Xamarin Forms 进度条控件 lindexi 2018-2-13 17:23:3 +0800 2018-2-1 ...

  9. Oracle单引号拼接和替换

    1.oracle拼接一个单引号: 正常写法:''''|| 由于单引号存在转义,第一个和最后一个是指定你要使用的字符,第二个’是单引号的转义字符,所以需要第三个‘才是真正你要拼接的那个. 也可以用 ch ...

  10. Appium+Pytest实现app并发测试

    前言 这个功能已经写完很长时间了,一直没有发出来,今天先把代码发出来吧,有一些代码是参考网上写的,具体的代码说明今天暂时先不发了,代码解释的太详细还得我花点时间^_^, 毕竟想让每个人都能看明白也不容 ...