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


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

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

Dispatcher.Yield

如果一个方法的实现比较耗时,为了不影响 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.YieldDispatcher 类型的静态方法,而不是像 InvokeAsync 一样是实例方法。不过 C# 有一个神奇的特性——静态方法和实例方法可以在同一上下文中调用,而不用担心产生歧义。

例如:

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

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

Task.Yield

拿前面 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)。

希望了解 DispatcherSynchronizationContext 的区别可以阅读 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 则可以做到将此任务分成很多个片段执行。


参考资料

  • https://stackoverflow.com/questions/23431595/task-yield-real-usages“>c# - Task.Yield - real usages? - Stack Overflow
  • Task.Yield Method (System.Threading.Tasks)
  • c# - Difference between Synchronization Context and Dispatcher - Stack Overflow

转载于:https://www.cnblogs.com/walterlv/p/10236535.html

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

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

    一个耗时的任务,可以通过 Task.Yield 或者 Dispatcher.Yield 来中断以便分割成多个小的任务片段执行. Yield 这个词很有意思,叫做"屈服"" ...

  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. MIT自然语言处理第三讲:概率语言模型(第一、二、三部分)
  2. Vue_VueResource
  3. leetcode 224. Basic Calculator | 224. 基本计算器(中缀表达式求值)
  4. java usb 无驱打印_Windows Usb 无驱动打印
  5. 【GRE协议】CentOS配置GRE隧道
  6. accp8.0 网页编程_某程序员月入上万!为何却说:我希望自己从来没有学过编程? - C/C++爱好者...
  7. 读《JavaScript权威指南》笔记(三)--对象
  8. 树莓派 口罩识别 python_RaspberryPi上实现佩戴口罩识别——2020电赛F题小记
  9. Creative Cloud Cleaner Tool mac卸载adobe系列软件详细教程
  10. NB-IoT 基于蜂窝的窄带物联网
  11. 携程机票业务数据仓库实践
  12. X86 CPU 漏洞 Meltdown 原理及google攻击代码
  13. 【理解】运用数据透视表制作三栏账
  14. Atlassian Bamboo入门安装与使用
  15. ERP : 总量库存管理
  16. python 取整函数
  17. HttpClient 实现 socks 代理
  18. 牛客网小白月赛5 H-最大公约数(lcm)
  19. XMail 安装配置使用
  20. hbase热点问题解决(预分区)

热门文章

  1. 清华大学计算机学院赵成钢哪里人,清华大学16位学霸简历吓坏网友
  2. zynqpl端时钟_第十二章 ZYNQ-MIZ702 PS读写PL端BRAM
  3. 爬虫的步骤解析内容xpath介绍_爬虫入门到精通-网页的解析(xpath)
  4. 【若依(ruoyi)】Bootstrap-Table表格排序
  5. mysql 将查询所得结果集的某一字段拼接成字符串
  6. apply和call
  7. 零拷贝和java NIO
  8. Apache Flink 零基础入门(九)Flink支持哪些数据类型
  9. why在重写equals时还必须重写hashcode方法
  10. 2021年互联网公司“死亡”名单!(附清单)