Yield
這個詞頗有意思,叫作「屈服」「放棄」「讓步」,字面意義上是讓出當前任務的執行權,轉而讓其餘任務能夠插入執行。Task
、Dispatcher
、Thread
都有 Yield()
方法,看起來均可以讓出當前任務的執行權。c#
若是在閱讀中發現對本文涉及到的一些概念不太明白,能夠閱讀:多線程
若是一個方法的實現比較耗時,爲了避免影響 UI 的響應,你會選擇用什麼方法呢?我以前介紹過的 Invoke 和 InvokeAsync 能夠解決,將後續耗時的任務分割成一個個小的片斷以低於用戶輸入和渲染的優先級執行。ide
Dispatcher.Yield
也能夠,其行爲更加相似於 Dispatcher.InvokeAsync
(即採用 Dispatcher
調度的方式,事實上後面會說到其實就是調用了 InvokeAsync
),而非 Dispatcher.Invoke
(即採用 PushFrame
新開消息循環的方式)。spa
使用時須要 await
:.net
foreach(var item in collection)
{
DoWorkWhichWillTakeHalfASecond();
await Dispatcher.Yield();
}
這樣,這個 foreach
將在每遍歷到一個集合項的時候中斷一次,讓 UI 可以響應用戶的交互輸入和渲染。pwa
Yield
方法能夠傳入一個優先級參數,指示繼續執行後續任務的優先級。默認是 DispatcherPriority.Background
,低於用戶輸入 DispatcherPriority.Input
、 UI 邏輯 DispatcherPriority.Loaded
和渲染 DispatcherPriority.Render
。線程
Dispatcher.Yield
是如何作到出讓執行權的呢?code
查看源碼,發現 DispatcherYield
的返回值是 DispatcherPriorityAwaiter
,而它的 OnCompleted
方法是這樣的:orm
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。xml
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
則能夠作到將此任務分紅不少個片斷執行。