C#中的Dispatcher


C#中BackgroundWorker

使用前提

在WPF程序中,有一些比較耗時的后台操作時,比如向遠程服務器請求數據,或者通過TCP/IP為某台設備提供升級固件服務等等。為了防止這類操作freeze用戶界面,造成用戶體驗下降,即程序假死的狀況出現。一種常見的,更user friendly的方式是,提供一個進度條窗口,提示用戶該操作的完成進度。並提供取消操作的選項。

C#中的 BackgroundWorker Class 則是執行該任務的最佳選擇。

The BackgroundWorker class allows you to run an operation on a separate, dedicated thread. Time-consuming operations like downloads and database transactions can cause your user interface (UI) to seem as though it has stopped responding while they are running. When you want a responsive UI and you are faced with long delays associated with such operations, theBackgroundWorker class provides a convenient solution.
-MSDN

關於 BackgroundWorker 類

主要屬性

  • CancellationPending - 只讀屬性,default值為false,執行CancelAsync方法后,值為true。表明應用程序請求了取消后台操作。
  • IsBusy - 如果后台異步操作開始執行,值為true,否則為false
  • WorkerReportProgress - 如果BackgroundWorker支持后台操作進程更新,設置值為true,default值為false

主要事件

  • DoWork
  • ProgressChanged
  • RunWorkerCompleted
    不要再DoWork事件處理程序中對UI線程中的對象進行操作,操作應該放在ProgressChanged和RunWorkerCompleted的事件處理程序中。

主要方法

  • RunWorkerAsync() - 執行后台操作,激發DoWork事件
  • ReportProgress()- 激發ProgressChanged事件
  • CancelAsync() - 提交終止后台操作的請求,並將CancellationPending屬性值設為true。在程序其他地方要定時檢查CancellationPending屬性的值,作出相應操作,比如
if (worker.CancellationPending) { e.Cancel = true; } 

示例程序

XAML

<Window x:Class="BackgroundWorkerExample.MainWindow"  
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
        Title="MainWindow" Height="150" Width="300">
    <StackPanel>
        <ProgressBar Name="progressBar" Height="20" Width="250" Margin="10"></ProgressBar>
        <TextBox Name="textBox" Width="50" Height="20" HorizontalAlignment="Center"></TextBox>
        <Button Name="btnProcess" Width="100" Click="btnProcess_Click" Margin="5">Start</Button>
        <Button Name="btnCancel" Width="100" Click="btnCancel_Click" Margin="5">Cancel</Button>
    </StackPanel>
</Window>

C#

namespace BackgroundWorkerExample { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { BackgroundWorker bgworker = new BackgroundWorker(); public MainWindow() { InitializeComponent(); bgworker.WorkerReportsProgress = true; bgworker.WorkerSupportsCancellation = true; bgworker.DoWork += bgworker_DoWork; bgworker.ProgressChanged += bgworker_ProgressChanged; bgworker.RunWorkerCompleted += bgworker_RunWorkerCompleted; } void bgworker_DoWork(object sender, DoWorkEventArgs e) { BackgroundWorker worker = sender as BackgroundWorker; for (int i = 1; i <= 100; i++) { if (worker.CancellationPending) { e.Cancel = true; } else { worker.ReportProgress(i); Thread.Sleep(100); } } } void bgworker_ProgressChanged(object sender, ProgressChangedEventArgs e) { progressBar.Value = e.ProgressPercentage; textBox.Text = e.ProgressPercentage.ToString(); } void bgworker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { progressBar.Value = 0; if (e.Cancelled) { MessageBox.Show("Background task has been canceled", "info"); } else { MessageBox.Show("Background task finished", "info"); } } private void btnProcess_Click(object sender, RoutedEventArgs e) { if (!bgworker.IsBusy) { bgworker.RunWorkerAsync(); } } private void btnCancel_Click(object sender, RoutedEventArgs e) { bgworker.CancelAsync(); } } } 

演示

 
BackgroundWorkerExample.gif


作者:Jason_Yuan
鏈接:https://www.jianshu.com/p/b89f39c5f803
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。

今天要說的是WPF程序員處理多線程的另外一個方式 - Dispatcher

當我們打開一個WPF應用程序即開啟了一個進程,該進程中至少包含兩個線程。

  • 一個線程用於處理呈現:隱藏在后台運行
  • 一個線程用於管理用戶界面:接收輸入、處理事件、繪制屏幕以及運行應用程序代碼。即UI線程。

在UI線程中有一個Dispatcher對象,管理每一個需要執行的工作項。Dispatcher會根據每個工作項的優先級排隊。向Dispatcher列隊中添加工作項時可指定10個不同的級別。那么問題來了,如果遇到耗時操作的時候,該操作如果依舊發生在UI線程中,Dispatcher 列隊中其他的需要執行的工作項都要等待,從而造成界面假死的現象。為了加快響應速度,提高用戶體驗,我們應該盡量保證Dispatcher 列隊中工作項要。所以,對於耗時操作,我們應該開辟一個新的子線程去處理,在操作完成后,通過向UI線程的Dispatcher列隊注冊工作項,來通知UI線程更新結果。

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

  • Dispatcher實際上並不是多線程
  • 子線程不能直接修改UI線程,必須通過向UI線程中的Dispatcher注冊工作項來完成
  • Dispatcher 是單例模式,暴露了一個靜態的CurrentDispatcher方法用於獲得當前線程的Dispatcher
  • 每一個UI線程都至少有一個Dispatcher,一個Dispatcher只能在一個線程中執行工作。
  • 開啟新線程的方法很多,比如delegate.BeginInvoke()的方式開啟的新線程。

Delegate.Invoke: Executes synchronously, on the same thread.
Delegate.BeginInvoke: Executes asynchronously, on a threadpool thread.

示例程序

XAML

<Window x:Class="DispatcherExample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="160" Width="300">
    <StackPanel>
        <ProgressBar Name="progressBar" Height="20" Width="250" Margin="10"></ProgressBar>
        <TextBox Name="textBox" Width="50" Height="20" HorizontalAlignment="Center"></TextBox>
        <Button Name="btnProcess" Width="100" Click="btnProcess_Click" Margin="5">Start</Button>
        <Button Name="btnCancel" Width="100" Click="btnCancel_Click" Margin="5">Cancel</Button>
    </StackPanel>
</Window>

C#

namespace DispatcherExample { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } Thread taskThread; private void btnProcess_Click(object sender, RoutedEventArgs e) { taskThread = new Thread(DoTask); taskThread.Start(); } private void btnCancel_Click(object sender, RoutedEventArgs e) { taskThread.Abort(); MessageBox.Show("Background task finished normally", "info"); this.progressBar.Value = 0; this.textBox.Text = null; } private void DoTask() { Int64 InputNum = (Int64)100; for (Int64 i = 0; i < InputNum; i++) { Thread.Sleep(100); this.Dispatcher.BeginInvoke((Action)delegate() { this.progressBar.Value = i; this.textBox.Text = i.ToString(); }); } MessageBox.Show("Background task has been canceled", "info"); this.Dispatcher.BeginInvoke((Action)delegate() { this.progressBar.Value = 0; this.textBox.Text = null; }); } } } 

演示

 
DispatcherExample.gif


作者:Jason_Yuan
鏈接:https://www.jianshu.com/p/0714fc755988
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。


免責聲明!

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



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