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


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

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 则可以做到将此任务分成很多个片段执行。


参考资料

出让执行权:Task.Yield, Dispathcer.Yield的更多相关文章

  1. “全栈2019”Java多线程第八章:放弃执行权yield()方法详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  2. 5-让出CPU执行权的yield方法

    让出CPU执行权的yield方法 Thread类有一个静态的yield方法,当一个线程在调用yield方法时,实际上就是暗示线程调度器请求让出自己的CPU使用,但是线程调度器可以无条件忽略这个暗示. ...

  3. ruby 疑难点之—— yield 和 yield self

    yield 所有的"方法(methods)"隐式跟上一个"块(block)"参数. 块参数也可以明确给定,形式就是在参数前面加一个"&&quo ...

  4. python yield 和 yield from用法总结

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

  5. python yield 与 yield from转

    python yield 与 yield from转 https://blog.csdn.net/chenbin520/article/details/78111399?locationNum=7&a ...

  6. yield与yield from

    yield 通过yield返回的是一个生成器,yield既可以产出值又可以生成值,yield可以用next()来启动生成器,同时可以用send向生成器传递值:在初次启动生成器时,需调用next()或s ...

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

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

  8. python yield、yield from与协程

    从生成器到协程 协程是指一个过程,这个过程与调用方协作,产出由调用方提供的值.生成器的调用方可以使用 .send(...)方法发送数据,发送的数据会成为yield表达式的值.因此,生成器可以作为协程使 ...

  9. python协程--yield和yield from

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

随机推荐

  1. angular js实现开关效果

    功能:实现点击排序,再点击排倒序. 实现方法如下 方法一:定义变量实现点击切换true或false,代码为:  $scope.lidata = [                {"name ...

  2. Unique Binary Search Trees,Unique Binary Search Trees2 生成二叉排序树

    Unique Binary Search Trees:求生成二叉排序树的个数. Given n, how many structurally unique BST's (binary search t ...

  3. Nordic官方网络资源介绍(官网/devzone/GitHub)

    本文将介绍Nordic官方网络资源,包括Nordic官网,开发者论坛(devzone),以及Nordic在GitHub上的共享资源. 1. Nordic官网(产品/SDK/工具/文档库) Nordic ...

  4. jQuery回车键快速切换下一个input文本框解决方案

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. POJ1247-Magnificent Meatballs

    http://poj.org/problem?id=1247 Magnificent Meatballs Time Limit: 1000MS   Memory Limit: 10000K Total ...

  6. Django框架学习笔记(windows环境下安装)

    博主最近开始学习主流框架django 网上大部分的安装环境都linux的 由于博主在windows环境下已经有了 Pycharm编辑器 ,所以决定还是继续在windows环境下学习 首先是下载 链接 ...

  7. Ghost:一款简约风格博客系统

    前言 本文将介绍一种最快速的创建Ghost博客系统的方法,并实现绑定二级域名到该博客系统.本文以本博客的“微博客”为例. 一键创建Ghost博客系统 Kite 是 Ghost 博客托管商,网址为:ht ...

  8. cJSON序列化工具解读一(结构剖析)

    cJSON简介 JSON基本信息 JSON(JavaScript Object Notation)是一种轻量级的数据交换格式.易于人阅读和编写.同时易于机器解析和生成.是一种很好地数据交换语言. 官方 ...

  9. Java提高篇之常量池

    一.相关概念 1. 什么是常量 用final修饰的成员变量表示常量,值一旦给定就无法改变! final修饰的变量有三种:静态变量.实例变量和局部变量,分别表示三种类型的常量. 2. Class文件中的 ...

  10. SPOJ-CLEANRBT-状压dp

    CLEANRBT - Cleaning Robot #dynamic-programming #bfs Here, we want to solve path planning for a mobil ...