WPF 線程 Dispatcher


WPF 應用程序從兩個線程開始:

一個用於處理呈現
一個用於管理 UI
呈現線程有效地隱藏在后台運行,而UI線程則接收輸入、處理事件、繪制屏幕以及運行應用程序代碼。
大多數應用程序都使用一個 UI 線程,但在某些情況下,最好使用多個線程。我們將在后面舉例說明這一點。

UI 線程對一個名為 Dispatcher 的對象內的工作項進行排隊。

Dispatcher基於優先級選擇工作項,並運行每一個工作項,直到完成。每個UI線程都必須至少有一個Dispatcher,並且每個 Dispatcher 都只能在一個線程中執行工作項。

要構建響應速度快、且用戶友好的應用程序,訣竅是減小工作項,以最大限度地提高Dispatcher吞吐量。這樣,工作項將永遠不會因為在Dispatcher隊列中等待處理而失效。輸入與響應之間的任何可察覺的延遲都會使用戶不快。

那么,WPF應用程序應如何處理大型操作呢?如果您的代碼涉及大型計算,或者需要查詢某台遠程服務器上的數據庫,應怎么辦呢?通常的辦法是在單獨的線程中處理大型操作,而專門讓UI線程來負責處理Dispatcher隊列中的工作項。當大型操作完成時,可以將結果報告給 UI 線程來顯示。

一直以來,Windows只允許創建UI元素的線程訪問這些元素。這意味着負責某項長時間運行任務的后台線程無法更新已完成的文本框。Windows 這樣做是為了確保 UI 組件的完整性。如果列表框的內容在繪制過程中被后台線程更新,那么該列表框看上去將會很奇怪。

WPF 使用一種內置互斥機制來強制執行這種協調。WPF 中的大多數類都派生自 DispatcherObject。DispatcherObject 在構造時存儲對鏈接到當前所運行線程的 Dispatcher 的引用。實際上,DispatcherObject與創建它的線程關聯。

在程序執行過程中,DispatcherObject 可以調用它的公共 VerifyAccess 方法。

VerifyAccess 檢查與當前線程關聯的 Dispatcher,並將它與構造過程中存儲的 Dispatcher 引用進行比較。

如果兩者不匹配,VerifyAccess將引發異常。VerifyAccess 用於在每個屬於 DispatcherObject 的方法的開頭調用。

如果只有一個線程可以修改 UI,那么后台線程如何與用戶交互呢?

后台線程可以請求 UI 線程代表它執行操作。這是通過向 UI 線程的 Dispatcher 注冊工作項來完成的。

Dispatcher 類提供兩個注冊工作項的方法:Invoke 和 BeginInvoke。
這兩個方法均調度一個委托來執行。
Invoke 是同步調用,也就是說,直到 UI 線程實際執行完該委托它才返回。
BeginInvoke 是異步的,將立即返回。

Dispatcher 按優先級對其隊列中的元素進行排序。向 Dispatcher 隊列中添加元素時可指定 10 個級別。
這些優先級在 DispatcherPriority 枚舉中維護。

DispatchPriority 優先級別

 

 

 

優先級

說明

Invalid

這是一個無效的優先級。

Inactive

工作項目已排隊但未處理。

SystemIdle

僅當系統空閑時才將工作項目調度到 UI 線程。這是實際得到處理的項目的最低優先級。

ApplicationIdle

僅當應用程序本身空閑時才將工作項目調度到 UI 線程。

ContextIdle

僅在優先級更高的工作項目得到處理后才將工作項目調度到 UI 線程。

Background

在所有布局、呈現和輸入項目都得到處理后才將工作項目調度到 UI 線程。

Input

以與用戶輸入相同的優先級將工作項目調度到 UI 線程。

Loaded

在所有布局和呈現都完成后才將工作項目調度到 UI 線程。

Render

以與呈現引擎相同的優先級將工作項目調度到 UI 線程。

DataBind

以與數據綁定相同的優先級將工作項目調度到 UI 線程。

Normal

以正常優先級將工作項目調度到 UI 線程。這是調度大多數應用程序工作項目時的優先級。

Send

以最高優先級將工作項目調度到 UI 線程。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

第1種用 Task類. 推薦用這個辦法

復制代碼
public void 工作_Task()
{
Dispatcher x = Dispatcher.CurrentDispatcher;//取得當前工作線程
//另開線程工作
Task<int> 計數 = new Task<int>(() => { return 計數方法(); });
計數.ContinueWith(工作完畢后方法);//工作完畢后執行的方法
計數.Start();//開始工作

}
public void工作完畢后方法(Task<int> 參數)
{
if (參數.IsCompleted) //正常工作完畢
{
var 結果 = 參數.Result; //取得結果
//處理結果.
//本方法非界面線程.如果需要在界面線程操作,需要轉移到界面線程
}
}

int c;
public int 計數方法()
{
return c++;
}
復制代碼

第2種方法用線程.

復制代碼
public void 工作_Thread()
{
Dispatcher x = Dispatcher.CurrentDispatcher;//取得當前工作線程
//另開線程工作
System.Threading.ThreadStart start = delegate()
{
//工作函數
Func<string> fu = new Func<string>(() => { return ""; });//工作函數
var 工作結果 = fu();//開始工作

//異步更新界面
x.BeginInvoke(new Action(() =>
{
//在界面線程操作 可以使用 工作結果
}), DispatcherPriority.Normal); 
};
new System.Threading.Thread(start).Start(); //啟動線程
}
復制代碼

第3種方法用 BackgroundWorker.

這種方法介紹的比較多了.就不說了.

復制代碼
BackgroundWorker 后台線程;
public void線程初始化()
{
后台線程 = new BackgroundWorker();
后台線程.WorkerSupportsCancellation = true; //可以取消
后台線程.DoWork += new DoWorkEventHandler(后台線程_DoWork);
后台線程.RunWorkerCompleted += new RunWorkerCompletedEventHandler(后台線程_RunWorkerCompleted); 
}
public void 啟動后台線程()
{
后台線程.RunWorkerAsync();
}

void 后台線程_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//工作完畢的方法
}

void 后台線程_DoWork(object sender, DoWorkEventArgs e)
{
//工作方法
}
復制代碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM