《WPF編程寶典》一個多線程示例,記錄下。
1、介紹:經過多線程特性可以使WPF應用程序執行後臺工做,同時保持用戶界面可以進行響應。
1 瞭解多線程模型
WPF元素具備線程關聯性:建立WPF元素的線程擁有全部所建立的元素,其餘線程不能直接與這些WPF元素進行交互
dispatcher(調度程序)擁有應用程序線程,並管理工做項隊列。當新線程第一次實例化DispatcherObject類的派生類時,會建立調度程序dispatch(WPF堅持一個用戶界面線程和一個調度線程)。
ps:
a、在傳遞給BeginInvoke()的方法中執行耗時的代碼是不合理的,以下第2個按鈕 sleep(5s)。
b、若是須要暫停異步操做指導用戶提供一些反饋信息,可以使用Invoke()方法。
c、BackgroundWorker組件爲在單獨線程中執行耗時的任務提供一種簡單方法,適合單個異步任務後臺運行,如算法。.net2.0提供。
d、若在整個應用程序生命週期中運行異步任務,或需與應用程序進行通訊時,須要使用.net的線程支持來自定義解決方案。
2 代碼
主界面xaml算法
<Window x:Class="Multithreading.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Multithreading" Height="300" Width="300" > <StackPanel Margin="5"> <TextBox Name="txt">Text in a text box.</TextBox> <Button Click="cmdBreakRules_Click">Break the Rules</Button> <Button Click="cmdFollowRules_Click">Follow the Rules (with BeginInvoke)</Button> <Button Click="cmdBackgroundWorker_Click">Use the BackgroundWorker</Button> </StackPanel> </Window>
一種在本調度程序創建異步執行委託:編程
private void cmdBreakRules_Click(object sender, RoutedEventArgs e) { this.Dispatcher.BeginInvoke(new UpdateTextExcute(UpdateTextWrong)); //Thread thread = new Thread(UpdateTextWrong);//報異常 //thread.Start(); } private void UpdateTextWrong() { this.txt.Text = "Here is some new text."; }
另外一種經過在新建的線程上,調用本調度程序dispatch執行委託:多線程
private void cmdFollowRules_Click(object sender, RoutedEventArgs e) { Thread thread = new Thread(UpdateTextRight); thread.Start(); } private void UpdateTextRight() { Thread.Sleep(TimeSpan.FromSeconds(5)); this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart) delegate() { txt.Text = "(5s):Here is some new text."; } ); }
最後一個有關BackgroundWork專用長時間算法的多線程異步
private void cmdBackgroundWorker_Click(object sender, RoutedEventArgs e) { BackgroundWorkerTest test = new BackgroundWorkerTest(); test.ShowDialog(); }
顯示另外一個窗口ide
<Window x:Class="Multithreading.BackgroundWorkerTest" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Multithreading" Height="323.2" Width="305.6" xmlns:cm="clr-namespace:System.ComponentModel;assembly=System" > <Window.Resources> <cm:BackgroundWorker x:Key="backgroundWorker" WorkerReportsProgress="True" WorkerSupportsCancellation="True" DoWork="backgroundWorker_DoWork" ProgressChanged="backgroundWorker_ProgressChanged" RunWorkerCompleted="backgroundWorker_RunWorkerCompleted"></cm:BackgroundWorker> </Window.Resources> <Grid Margin="5"> <Grid.RowDefinitions> <RowDefinition Height="Auto"></RowDefinition> <RowDefinition Height="Auto"></RowDefinition> <RowDefinition Height="Auto"></RowDefinition> <RowDefinition></RowDefinition> <RowDefinition Height="Auto"></RowDefinition> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"></ColumnDefinition> <ColumnDefinition></ColumnDefinition> </Grid.ColumnDefinitions> <TextBlock Margin="5">From:</TextBlock> <TextBox Name="txtFrom" Grid.Column="1" Margin="5">1</TextBox> <TextBlock Grid.Row="1" Margin="5">To:</TextBlock> <TextBox Name="txtTo" Grid.Row="1" Grid.Column="1" Margin="5">500000</TextBox> <StackPanel Orientation="Horizontal" Grid.Row="2" Grid.Column="1"> <Button Name="cmdFind" Margin="5" Padding="3" Click="cmdFind_Click">Find Primes</Button> <Button Name="cmdCancel" Margin="5" Padding="3" IsEnabled="False" Click="cmdCancel_Click">Cancel</Button> </StackPanel> <TextBlock Grid.Row="3" Margin="5">Results:</TextBlock> <ListBox Name="lstPrimes" Grid.Row="3" Grid.Column="1" Margin="5"></ListBox> <ProgressBar Name="progressBar" Grid.Row="4" Grid.ColumnSpan="2" Margin="5" VerticalAlignment="Bottom" MinHeight="20" Minimum="0" Maximum="100" Height="20"></ProgressBar> </Grid> </Window>
後臺代碼:this
using System; using System.Windows; using System.ComponentModel; namespace Multithreading { /// <summary> /// Interaction logic for BackgroundWorkerTest.xaml /// </summary> public partial class BackgroundWorkerTest : System.Windows.Window { public BackgroundWorkerTest() { InitializeComponent(); backgroundWorker = ((BackgroundWorker)this.FindResource("backgroundWorker")); } private BackgroundWorker backgroundWorker; private void cmdFind_Click(object sender, RoutedEventArgs e) { // Disable the button and clear previous results. cmdFind.IsEnabled = false; cmdCancel.IsEnabled = true; lstPrimes.Items.Clear(); // Get the search range. int from, to; if (!Int32.TryParse(txtFrom.Text, out from)) { MessageBox.Show("Invalid From value."); return; } if (!Int32.TryParse(txtTo.Text, out to)) { MessageBox.Show("Invalid To value."); return; } // Start the search for primes on another thread. FindPrimesInput input = new FindPrimesInput(from, to); backgroundWorker.RunWorkerAsync(input); } private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) { // Get the input values. FindPrimesInput input = (FindPrimesInput)e.Argument; // Start the search for primes and wait. int[] primes = Worker.FindPrimes(input.From, input.To, backgroundWorker); if (backgroundWorker.CancellationPending) { e.Cancel = true; return; } // Return the result. e.Result = primes; } private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { if (e.Cancelled) { MessageBox.Show("Search cancelled."); } else if (e.Error != null) { // An error was thrown by the DoWork event handler. MessageBox.Show(e.Error.Message, "An Error Occurred"); } else { int[] primes = (int[])e.Result; foreach (int prime in primes) { lstPrimes.Items.Add(prime); } } cmdFind.IsEnabled = true; cmdCancel.IsEnabled = false; progressBar.Value = 0; } private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) { progressBar.Value = e.ProgressPercentage; } private void cmdCancel_Click(object sender, RoutedEventArgs e) { backgroundWorker.CancelAsync(); } } }
數據類:spa
public class FindPrimesInput { public int To { get; set; } public int From { get; set; } public FindPrimesInput(int from, int to) { To = to; From = from; } }
有關進度條代碼:.net
#region Using directives using System; #endregion namespace Multithreading { public class Worker { public static int[] FindPrimes(int fromNumber, int toNumber) { return FindPrimes(fromNumber, toNumber, null); } public static int[] FindPrimes(int fromNumber, int toNumber, System.ComponentModel.BackgroundWorker backgroundWorker) { int[] list = new int[toNumber - fromNumber]; // Create an array containing all integers between the two specified numbers. for (int i = 0; i < list.Length; i++) { list[i] = fromNumber; fromNumber += 1; } //find out the module for each item in list, divided by each d, where //d is < or == to sqrt(to) //if the remainder is 0, the nubmer is a composite, and thus //we mark its position with 0 in the marks array, //otherwise the number is a prime, and thus mark it with 1 int maxDiv = (int)Math.Floor(Math.Sqrt(toNumber)); int[] mark = new int[list.Length]; for (int i = 0; i < list.Length; i++) { for (int j = 2; j <= maxDiv; j++) { if ((list[i] != j) && (list[i] % j == 0)) { mark[i] = 1; } } int iteration = list.Length / 100; if ((i % iteration == 0) && (backgroundWorker != null)) { if (backgroundWorker.CancellationPending) { // Return without doing any more work. return null; } if (backgroundWorker.WorkerReportsProgress) { //float progress = ((float)(i + 1)) / list.Length * 100; backgroundWorker.ReportProgress(i / iteration); //(int)Math.Round(progress)); } } } //create new array that contains only the primes, and return that array int primes = 0; for (int i = 0; i < mark.Length; i++) { if (mark[i] == 0) primes += 1; } int[] ret = new int[primes]; int curs = 0; for (int i = 0; i < mark.Length; i++) { if (mark[i] == 0) { ret[curs] = list[i]; curs += 1; } } if (backgroundWorker != null && backgroundWorker.WorkerReportsProgress) { backgroundWorker.ReportProgress(100); } return ret; } } }
執行結果:線程
以上。code