首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

WPF线程处置模型

2012-09-03 
WPF线程处理模型?概述和调度程序操作中的线程:示例具有长时间运行计算的单线程应用程序“Start”(开始)按钮

WPF线程处理模型

?

WPF线程处置模型概述和调度程序

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 Namespace
C#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 Sub
C#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 = False
C#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”(获取预报)按钮。

      多个窗口,多个线程XAML
      <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();}

      WPF线程处置模型技术细节和难点


      使用线程编写组件FetchWeatherFromServer?方法打包到一个可重用的非图形组件中。如果采用标准的 Microsoft .NET Framework 模式,那么代码应与下面的内容类似。

      C#VB
      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 的任务是避免意外的重新进入,不重新引入内存泄漏。这就是我们在任何位置都不阻止重新进入的原因。

热点排行