WPF线程处理模型
?
概述和调度程序
操作中的线程:示例
“Start”(开始)按钮时,搜索开始。?
XAML
<Window x:Class="SDKSamples.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Prime Numbers" Width="260" Height="75" > <StackPanel Orientation="Horizontal" VerticalAlignment="Center" > <Button Content="Start" Click="StartOrStop" Name="startStopButton" Margin="5,0,5,0" /> <TextBlock Margin="10,5,0,0">Biggest Prime Found:</TextBlock> <TextBlock Name="bigPrime" Margin="4,5,0,0">3</TextBlock> </StackPanel></Window>XAML
<Window x:Class="SDKSamples.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Prime Numbers" Width="260" Height="75" > <StackPanel Orientation="Horizontal" VerticalAlignment="Center" > <Button Content="Start" Click="StartOrStop" Name="startStopButton" Margin="5,0,5,0" /> <TextBlock Margin="10,5,0,0">Biggest Prime Found:</TextBlock> <TextBlock Name="bigPrime" Margin="4,5,0,0">3</TextBlock> </StackPanel></Window>
VB
Imports SystemImports System.WindowsImports System.Windows.ControlsImports System.Windows.ThreadingImports System.ThreadingNamespace SDKSamples Partial Public Class MainWindow Inherits Window Public Delegate Sub NextPrimeDelegate() 'Current number to check Private num As Long = 3 Private continueCalculating As Boolean = False Public Sub New() MyBase.New() InitializeComponent() End Sub Private Sub StartOrStop(ByVal sender As Object, ByVal e As EventArgs) If continueCalculating Then continueCalculating = False startStopButton.Content = "Resume" Else continueCalculating = True startStopButton.Content = "Stop" startStopButton.Dispatcher.BeginInvoke(DispatcherPriority.Normal, New NextPrimeDelegate(AddressOf CheckNextNumber)) End If End Sub Public Sub CheckNextNumber() ' Reset flag. NotAPrime = False For i As Long = 3 To Math.Sqrt(num) If num Mod i = 0 Then ' Set not a prime flag to true. NotAPrime = True Exit For End If Next ' If a prime number. If Not NotAPrime Then bigPrime.Text = num.ToString() End If num += 2 If continueCalculating Then startStopButton.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.SystemIdle, New NextPrimeDelegate(AddressOf Me.CheckNextNumber)) End If End Sub Private NotAPrime As Boolean = False End ClassEnd NamespaceC#VB
using System;using System.Windows;using System.Windows.Controls;using System.Windows.Threading;using System.Threading;namespace SDKSamples{ public partial class Window1 : Window { public delegate void NextPrimeDelegate(); //Current number to check private long num = 3; private bool continueCalculating = false; public Window1() : base() { InitializeComponent(); } private void StartOrStop(object sender, EventArgs e) { if (continueCalculating) { continueCalculating = false; startStopButton.Content = "Resume"; } else { continueCalculating = true; startStopButton.Content = "Stop"; startStopButton.Dispatcher.BeginInvoke( DispatcherPriority.Normal, new NextPrimeDelegate(CheckNextNumber)); } } public void CheckNextNumber() { // Reset flag. NotAPrime = false; for (long i = 3; i <= Math.Sqrt(num); i++) { if (num % i == 0) { // Set not a prime flag to true. NotAPrime = true; break; } } // If a prime number. if (!NotAPrime) { bigPrime.Text = num.ToString(); } num += 2; if (continueCalculating) { startStopButton.Dispatcher.BeginInvoke( System.Windows.Threading.DispatcherPriority.SystemIdle, new NextPrimeDelegate(this.CheckNextNumber)); } } private bool NotAPrime = false; }}VB
Private Sub StartOrStop(ByVal sender As Object, ByVal e As EventArgs) If continueCalculating Then continueCalculating = False startStopButton.Content = "Resume" Else continueCalculating = True startStopButton.Content = "Stop" startStopButton.Dispatcher.BeginInvoke(DispatcherPriority.Normal, New NextPrimeDelegate(AddressOf CheckNextNumber)) End IfEnd SubC#VB
private void StartOrStop(object sender, EventArgs e){ if (continueCalculating) { continueCalculating = false; startStopButton.Content = "Resume"; } else { continueCalculating = true; startStopButton.Content = "Stop"; startStopButton.Dispatcher.BeginInvoke( DispatcherPriority.Normal, new NextPrimeDelegate(CheckNextNumber)); }}VB
Public Sub CheckNextNumber() ' Reset flag. NotAPrime = False For i As Long = 3 To Math.Sqrt(num) If num Mod i = 0 Then ' Set not a prime flag to true. NotAPrime = True Exit For End If Next ' If a prime number. If Not NotAPrime Then bigPrime.Text = num.ToString() End If num += 2 If continueCalculating Then startStopButton.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.SystemIdle, New NextPrimeDelegate(AddressOf Me.CheckNextNumber)) End IfEnd SubPrivate NotAPrime As Boolean = FalseC#VB
public void CheckNextNumber(){ // Reset flag. NotAPrime = false; for (long i = 3; i <= Math.Sqrt(num); i++) { if (num % i == 0) { // Set not a prime flag to true. NotAPrime = true; break; } } // If a prime number. if (!NotAPrime) { bigPrime.Text = num.ToString(); } num += 2; if (continueCalculating) { startStopButton.Dispatcher.BeginInvoke( System.Windows.Threading.DispatcherPriority.SystemIdle, new NextPrimeDelegate(this.CheckNextNumber)); }}private bool NotAPrime = false;bigPrimeTextBlock?来反映搜索结果。?用后台线程处理阻止操作C#VB
using System;using System.Windows;using System.Windows.Controls;using System.Windows.Media;using System.Windows.Media.Animation;using System.Windows.Media.Imaging;using System.Windows.Shapes;using System.Windows.Threading;using System.Threading;namespace SDKSamples{ public partial class Window1 : Window { // Delegates to be used in placking jobs onto the Dispatcher. private delegate void NoArgDelegate(); private delegate void OneArgDelegate(String arg); // Storyboards for the animations. private Storyboard showClockFaceStoryboard; private Storyboard hideClockFaceStoryboard; private Storyboard showWeatherImageStoryboard; private Storyboard hideWeatherImageStoryboard; public Window1(): base() { InitializeComponent(); } private void Window_Loaded(object sender, RoutedEventArgs e) { // Load the storyboard resources. showClockFaceStoryboard = (Storyboard)this.Resources["ShowClockFaceStoryboard"]; hideClockFaceStoryboard = (Storyboard)this.Resources["HideClockFaceStoryboard"]; showWeatherImageStoryboard = (Storyboard)this.Resources["ShowWeatherImageStoryboard"]; hideWeatherImageStoryboard = (Storyboard)this.Resources["HideWeatherImageStoryboard"]; } private void ForecastButtonHandler(object sender, RoutedEventArgs e) { // Change the status image and start the rotation animation. fetchButton.IsEnabled = false; fetchButton.Content = "Contacting Server"; weatherText.Text = ""; hideWeatherImageStoryboard.Begin(this); // Start fetching the weather forecast asynchronously. NoArgDelegate fetcher = new NoArgDelegate( this.FetchWeatherFromServer); fetcher.BeginInvoke(null, null); } private void FetchWeatherFromServer() { // Simulate the delay from network access. Thread.Sleep(4000); // Tried and true method for weather forecasting - random numbers. Random rand = new Random(); String weather; if (rand.Next(2) == 0) { weather = "rainy"; } else { weather = "sunny"; } // Schedule the update function in the UI thread. tomorrowsWeather.Dispatcher.BeginInvoke( System.Windows.Threading.DispatcherPriority.Normal, new OneArgDelegate(UpdateUserInterface), weather); } private void UpdateUserInterface(String weather) { //Set the weather image if (weather == "sunny") { weatherIndicatorImage.Source = (ImageSource)this.Resources[ "SunnyImageSource"]; } else if (weather == "rainy") { weatherIndicatorImage.Source = (ImageSource)this.Resources[ "RainingImageSource"]; } //Stop clock animation showClockFaceStoryboard.Stop(this); hideClockFaceStoryboard.Begin(this); //Update UI text fetchButton.IsEnabled = true; fetchButton.Content = "Fetch Forecast"; weatherText.Text = weather; } private void HideClockFaceStoryboard_Completed(object sender, EventArgs args) { showWeatherImageStoryboard.Begin(this); } private void HideWeatherImageStoryboard_Completed(object sender, EventArgs args) { showClockFaceStoryboard.Begin(this, true); } }}C#VB
private void ForecastButtonHandler(object sender, RoutedEventArgs e){ // Change the status image and start the rotation animation. fetchButton.IsEnabled = false; fetchButton.Content = "Contacting Server"; weatherText.Text = ""; hideWeatherImageStoryboard.Begin(this); // Start fetching the weather forecast asynchronously. NoArgDelegate fetcher = new NoArgDelegate( this.FetchWeatherFromServer); fetcher.BeginInvoke(null, null);}FetchWeatherFromServer?方法,然后返回,这样?Dispatcher?就可以在我们等待收集天气预报时处理事件。
C#VB
private void FetchWeatherFromServer(){ // Simulate the delay from network access. Thread.Sleep(4000); // Tried and true method for weather forecasting - random numbers. Random rand = new Random(); String weather; if (rand.Next(2) == 0) { weather = "rainy"; } else { weather = "sunny"; } // Schedule the update function in the UI thread. tomorrowsWeather.Dispatcher.BeginInvoke( System.Windows.Threading.DispatcherPriority.Normal, new OneArgDelegate(UpdateUserInterface), weather);}UpdateUserInterface?的调用。我们将一个描述天气的字符串传递给安排的此方法调用。
C#VB
private void UpdateUserInterface(String weather){ //Set the weather image if (weather == "sunny") { weatherIndicatorImage.Source = (ImageSource)this.Resources[ "SunnyImageSource"]; } else if (weather == "rainy") { weatherIndicatorImage.Source = (ImageSource)this.Resources[ "RainingImageSource"]; } //Stop clock animation showClockFaceStoryboard.Stop(this); hideClockFaceStoryboard.Begin(this); //Update UI text fetchButton.IsEnabled = true; fetchButton.Content = "Fetch Forecast"; weatherText.Text = weather; }UpdateUserInterface?执行预定调用。此方法停止时钟动画并选择一个图像来描述天气。它显示此图像并还原“fetch forecast”(获取预报)按钮。
<Windowx:Class="SDKSamples.Window1"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="MultiBrowse"Height="600"Width="800"Loaded="OnLoaded"><StackPanelName="Stack"Orientation="Vertical"><StackPanelOrientation="Horizontal"><ButtonContent="New Window"Click="NewWindowHandler"/><TextBoxName="newLocation"Width="500"/><ButtonContent="GO!"Click="Browse"/></StackPanel><FrameName="placeHolder"Width="800"Height="550"></Frame></StackPanel></Window>C#VB
using System;using System.Windows;using System.Windows.Controls;using System.Windows.Data;using System.Windows.Threading;using System.Threading;namespace SDKSamples{ publicpartialclass Window1 : Window { public Window1() : base() { InitializeComponent(); } privatevoid OnLoaded(object sender, RoutedEventArgs e) { placeHolder.Source = new Uri("http://www.msn.com"); } privatevoid Browse(object sender, RoutedEventArgs e) { placeHolder.Source = new Uri(newLocation.Text); } privatevoid NewWindowHandler(object sender, RoutedEventArgs e) { Thread newWindowThread = new Thread(new ThreadStart(ThreadStartingPoint)); newWindowThread.SetApartmentState(ApartmentState.STA); newWindowThread.IsBackground = true; newWindowThread.Start(); } privatevoid ThreadStartingPoint() { Window1 tempWindow = new Window1(); tempWindow.Show(); System.Windows.Threading.Dispatcher.Run(); } }}C#VB
private void NewWindowHandler(object sender, RoutedEventArgs e){ Thread newWindowThread = new Thread(new ThreadStart(ThreadStartingPoint)); newWindowThread.SetApartmentState(ApartmentState.STA); newWindowThread.IsBackground = true; newWindowThread.Start();}C#VB
private void ThreadStartingPoint(){ Window1 tempWindow = new Window1(); tempWindow.Show(); System.Windows.Threading.Dispatcher.Run();}
技术细节和难点
public class WeatherComponent : Component{ //gets weather: Synchronous public string GetWeather() { string weather = ""; //predict the weather return weather; } //get weather: Asynchronous public void GetWeatherAsync() { //get the weather } public event GetWeatherCompletedEventHandler GetWeatherCompleted;}public class GetWeatherCompletedEventArgs : AsyncCompletedEventArgs{ public GetWeatherCompletedEventArgs(Exception error, bool canceled, object userState, string weather) : base(error, canceled, userState) { _weather = weather; } public string Weather { get { return _weather; } } private string _weather;}public delegate void GetWeatherCompletedEventHandler(object sender, GetWeatherCompletedEventArgs e);GetWeatherAsync?将使用前面介绍的一种技术(如创建后台线程)来异步执行工作,同时不阻止调用线程。
方法名称Async?方法的线程上调用方法名称Completed?方法。?C#VB
public class WeatherComponent2 : Component{ public string GetWeather() { return fetchWeatherFromServer(); } private DispatcherSynchronizationContext requestingContext = null; public void GetWeatherAsync() { if (requestingContext != null) throw new InvalidOperationException("This component can only handle 1 async request at a time"); requestingContext = (DispatcherSynchronizationContext)DispatcherSynchronizationContext.Current; NoArgDelegate fetcher = new NoArgDelegate(this.fetchWeatherFromServer); // Launch thread fetcher.BeginInvoke(null, null); } private void RaiseEvent(GetWeatherCompletedEventArgs e) { if (GetWeatherCompleted != null) GetWeatherCompleted(this, e); } private string fetchWeatherFromServer() { // do stuff string weather = ""; GetWeatherCompletedEventArgs e = new GetWeatherCompletedEventArgs(null, false, null, weather); SendOrPostCallback callback = new SendOrPostCallback(DoEvent); requestingContext.Post(callback, e); requestingContext = null; return e.Weather; } private void DoEvent(object e) { //do stuff } public event GetWeatherCompletedEventHandler GetWeatherCompleted; public delegate string NoArgDelegate();}失效的路由事件XAML
<Canvas MouseLeftButtonDown="handler1" Width="100" Height="100" > <Ellipse Width="50" Height="50" Fill="Blue" Canvas.Left="30" Canvas.Top="50" MouseLeftButtonDown="handler2" /></Canvas>
handler2。?handler2?结束后,事件将传递到?Canvas?对象,后者使用?handler1?来处理它。?handler2?未显式地将事件对象标记为已处理时,才会发生这种情况。
handler2?可能需要大量的时间来处理此事件。?handler2?使用?PushFrame?开始的嵌套消息循环可能在数小时内都不会返回。?handler2?仍未将事件标记为已处理,那么即便事件已经非常陈旧,仍将沿树向上传递。
Finalize?方法。问题就在这里:在 UI 线程上创建的 COM STA 对象只能在 UI 线程上释放。CLR 执行?BeginInvoke?的等效功能(在这种情况下使用 Win32 的?SendMessage)。但是,如果 UI 线程繁忙,则终结器线程将停止,COM STA 对象无法释放,这样会导致严重的内存泄漏。因此,CLR 团队进行严格的调用来使锁按预期的方式工作。??
WPF 的任务是避免意外的重新进入,不重新引入内存泄漏。这就是我们在任何位置都不阻止重新进入的原因。