一个耗时的任务,可以通过 Task.Yield 或者 Dispatcher.Yield 来中断以便分割成多个小的任务片段执行。

Yield 这个词很有意思,叫做“屈服”“放弃”“让步”,字面意义上是让出当前任务的执行权,转而让其他任务可以插入执行。Task、Dispatcher、Thread 都有 Yield() 方法,看起来都可以让出当前任务的执行权。


本文内容
  • Dispatcher.Yield

    • 需要注意

  • Task.Yield

如果在阅读中发现对本文涉及到的一些概念不太明白,可以阅读:

  • 深入了解 WPF Dispatcher 的工作原理(Invoke/InvokeAsync 部分)

  • 深入了解 WPF Dispatcher 的工作原理(PushFrame 部分)

如果一个方法的实现比较耗时,为了不影响 UI 的响应,你会选择用什么方法呢?我之前介绍过的 Invoke 和 InvokeAsync 可以解决,将后续耗时的任务分割成一个个小的片段以低于用户输入和渲染的优先级执行。

Dispatcher.Yield 也可以,其行为更加类似于 Dispatcher.InvokeAsync(即采用 Dispatcher 调度的方式,事实上后面会说到其实就是调用了 InvokeAsync),而非 Dispatcher.Invoke(即采用 PushFrame 新开消息循环的方式)。

使用时需要 await:

foreach(var item in collection){    DoWorkWhichWillTakeHalfASecond();    await Dispatcher.Yield();}

这样,这个 foreach 将在每遍历到一个集合项的时候中断一次,让 UI 能够响应用户的交互输入和渲染。

Yield 方法可以传入一个优先级参数,指示继续执行后续任务的优先级。默认是 DispatcherPriority.Background,低于用户输入 DispatcherPriority.Input、 UI 逻辑 DispatcherPriority.Loaded 和渲染 DispatcherPriority.Render。

Dispatcher.Yield 是如何做到出让执行权的呢?

查看源码,发现 DispatcherYield 的返回值是 DispatcherPriorityAwaiter,而它的 OnCompleted 方法是这样的:

public void OnCompleted(Action continuation){    if(_dispatcher == null)        throw new InvalidOperationException(SR.Get(SRID.DispatcherPriorityAwaiterInvalid));    _dispatcher.InvokeAsync(continuation, _priority);}

所以,其实真的就是 InvokeAsync。如果希望了解为何是 OnCompleted 方法,可以阅读 【C#】【多线程】【05-使用C#6.0】08-自定义awaitable类型 - L.M。

Dispatcher.Yield 是 Dispatcher 类型的静态方法,而不是像 InvokeAsync一样是实例方法。不过 C# 有一个神奇的特性——静态方法和实例方法可以在同一上下文中调用,而不用担心产生歧义。

例如:

using System.Windows.Threading;

class Demo : DispatcherObject{    void Test()    {        // 调用静态方法 Yield。        await Dispatcher.Yield();        // 调用实例方法 InvokeAsync。        await Dispatcher.InvokeAsync(() => { });    }}

注意需要引用命名空间 System.Windows.Threading。

拿前面 Dispatcher.Yield 的例子,我们换成 Task.Yield:

foreach(var item in collection){    DoWorkWhichWillTakeHalfASecond();    await Task.Yield();}

效果与 Dispatcher.Yield(DispatcherPriority.Normal) 是一样的。因为 Task 调度回到线程上下文靠的是 SynchronizationContext,WPF UI 线程的 SynchronizationContext 被设置为了 DispatcherSynchronizationContext,使用 Dispatcher 调度;而 DispatcherSynchronizationContext 构造时传入的优先级默认是 Normal,WPF 并没有特殊传入一个别的值,所以 WPF UI 线程上使用 Task.Yield() 出让执行权后,恢复时使用的是 Normal 优先级,相当于 Dispatcher.Yield(DispatcherPriority.Normal)。

希望了解 Dispatcher 和 SynchronizationContext 的区别可以阅读 c# - Difference between Synchronization Context and Dispatcher - Stack Overflow。

DispatcherSynchronizationContext 执行 await 后续任务的上下文代码:

/// <summary>///     Asynchronously invoke the callback in the SynchronizationContext./// </summary>public override void Post(SendOrPostCallback d, Object state){    // Call BeginInvoke with the cached priority.  Note that BeginInvoke    // preserves the behavior of passing exceptions to    // Dispatcher.UnhandledException unlike InvokeAsync.  This is    // desireable because there is no way to await the call to Post, so    // exceptions are hard to observe.    _dispatcher.BeginInvoke(_priority, d, state);}

既然是 Normal 优先级,那么在 UI 线程上的效果自然不如 Dispatcher.Yield。但是,Task.Yield 适用于任何线程,因为 SynchronizationContext 本身是与 Dispatcher 无关的,适用于任何线程。这样,于如果一个 Task 内部的任务太耗时,用 Task.Yield 则可以做到将此任务分成很多个片段执行。

如果觉得 Task.Yield() 的用途难以理解,可以参考 dudu 的博客 终于明白了 C# 中 Task.Yield 的用途 - dudu - 博客园。


参考资料

  • c# - Task.Yield - real usages? - Stack Overflow

  • Task.Yield Method (System.Threading.Tasks)

  • c# - Difference between Synchronization Context and Dispatcher - Stack Overflow

原文地址:https://walterlv.com/post/yield-in-task-dispatcher.html

.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com 

出让执行权:Task.Yield, Dispatcher.Yield相关推荐

  1. 出让执行权:Task.Yield, Dispathcer.Yield

    Yield 这个词很有意思,叫做"屈服""放弃""让步",字面意义上是让出当前任务的执行权,转而让其他任务可以插入执行.Task.Dispa ...

  2. Python中yield和yield from的用法

    yield 后面接的是 future 对象 调用方 委托生成器 yield from 直接给出循环后的结果 yield from 委托者和子生成器直接通信 yield from 直接处理stopIte ...

  3. python yield 和 yield from用法总结

    #例1. 简单输出斐波那契數列前 N 个数 #缺点:该函数可复用性较差,因为 fab 函数返回 None,其他函数无法获得该函数生成的数列 #要提高 fab 函数的可复用性,最好不要直接打印出数列,而 ...

  4. python的yield和yield from

    yield 为了理解什么是 yield,你必须理解什么是生成器.在理解生成器之前,让我们先知道什么是迭代. 可迭代对象 当你建立了一个列表,你可以逐项地读取这个列表,这叫做一个可迭代对象: >& ...

  5. python yield from_简述 yield 和 yield from

    yield 函数可以看成是一堆指令的集合.在函数中加入yield可以把一个函数变成一个generator,虽然调用的方式不一样了,但是其实现的功能和原来的函数基本是一样的. 而yield在这其中的作用 ...

  6. python协程--yield和yield from

    字典为动词"to yield"给出了两个释义:产出和让步.对于 Python 生成器中的 yield 来说,这两个含义都成立.yield item 这行代码会产出一个值,提供给 n ...

  7. 从yield 到yield from再到python协程

    yield 关键字 def fib():a, b = 0, 1while 1:yield ba, b = b, a+b yield 是在:PEP 255 -- Simple Generators 这个 ...

  8. yield 和 yield*

    yield yield 关键字用来暂停和恢复一个生成器函数(function*) 举例: function* gen(index) {while (index < 2) {yield index ...

  9. 从yield到yield from

    yield 带有 yield 的函数不再是一个普通函数,而是一个生成器generator 把生成器当作管道 def add_A(seq):for item in seq:yield item + '- ...

最新文章

  1. 尾气冒黑烟是什么问题_对机动车环保检测尾气排放的了解!
  2. [CF460E]Roland and Rose
  3. url映射 路由urls.py的功能 django
  4. matplotlib——在 Jupyter Notebook中绘制图像时只显示变量信息不显示图片
  5. java导出数据EXCEL的工具类(以spring-webmvc-4.0.4jar为基础)
  6. Android系列之开发环境搭建
  7. Mysql 分片,大数据量时扩容方案
  8. 常用0x000000类型颜色代码表
  9. linux虚拟机双显卡,Kali Linux 2.0 安装 NVIDIA显卡驱动实现双显卡(联想笔记本)
  10. 木兰词-人生若只如初见-纳兰性德
  11. 使用GPU硬件加速FFmpeg视频转码
  12. python中安装excel使用的模块
  13. 数据结构:利用栈实现数制转换
  14. 新开的淘宝店铺要怎么操作才能排名靠前?
  15. 1.清除最近使用过的文档记录
  16. MySQL分库分表解决方案
  17. java从入门到放弃,学习路线导航(附学习资源)
  18. RabbitMQ——邮件服务,初学记录
  19. jQuery实现Windows扑克牌小游戏代码
  20. 2019.9.3 随笔(2):智能工厂的主要技术

热门文章

  1. 在Windows 7中的Windows Media Player 12中快速预览歌曲
  2. 科研绘图工具软件_如何在Windows 10 Mail中使用绘图工具
  3. raspberry pi_在月光下将Raspberry Pi变成蒸汽机
  4. 关于jHipster框架在构建中的出现的error修复
  5. Linux之ACL权限控制
  6. HTML5程序开发范例宝典 完整版 (韩旭等著) 中文pdf扫描版
  7. Android数据库Realm实践
  8. 软件工程心理学之1----开篇
  9. WPF 实现验证码控件
  10. dotnet-httpie 0.2.0 Released