《WPF编程宝典》一个多线程示例,记录下。
一、介绍:通过多线程特性可使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();
}
显示另一个窗口
<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>
后台代码:
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();
}
}
}
数据类:
public class FindPrimesInput
{
public int To
{ get; set; }
public int From
{ get; set; }
public FindPrimesInput(int from, int to)
{
To = to;
From = from;
}
}
有关进度条代码:
#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;
}
}
}
执行结果:
以上。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。