WPF:(2)Thread線程樣例

源項目地址:https://github.com/Microsoft/...
如下是把樣例轉換爲簡要說明,同時給出實際運行效果及關鍵代碼:git

1 MultiThreadingWebBrowser

clipboard.png

private void Browse(object sender, RoutedEventArgs e)
{
    placeHolder.Source = new Uri(newLocation.Text);
}

private void NewWindowHandler(object sender, RoutedEventArgs e)
{
    var newWindowThread = new Thread(ThreadStartingPoint);
    newWindowThread.SetApartmentState(ApartmentState.STA);
    newWindowThread.IsBackground = true;
    newWindowThread.Start();
}

private void ThreadStartingPoint()
{
    var tempWindow = new MainWindow();
    tempWindow.Show();
    Dispatcher.Run();
    
    //Dispatcher.BeginInvoke(DispatcherPriority.Normal,
    //(ThreadStart)delegate ()
    //{
    //    var temWindow = new MainWindow();
    //    temWindow.Show();
    //});
}

2 SingleThreadedApplication

clipboard.png

調用自身線程循環查找素數
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()
{
    Stopwatch x = new Stopwatch();
    x.Start();

    // Reset flag.
    _notAPrime = false;

    for (long i = 3; i <= Math.Sqrt(_num); i++)
    {
        if (_num%i == 0)
        {
            // Set not a prime flag to ture.
            _notAPrime = true;
            break;
        }
    }

    // If a prime number.
    if (!_notAPrime)
    {
        x.Stop();
        elapsed.Text = x.ElapsedTicks.ToString();                
        bigPrime.Text = _num.ToString();
    }

    _num += 2;
    if (_continueCalculating)
    {
        startStopButton.Dispatcher.BeginInvoke(
            DispatcherPriority.SystemIdle,
            new NextPrimeDelegate(CheckNextNumber));
    }
}

3 UsingDispatcher天氣預報

clipboard.png

在本示例中,模擬檢索天氣預報的遠程過程調用。 使用一個單獨的輔助線程來執行此調用,並在完成後在 UI 線程的 Dispatcher 中調度一個更新方法。github

  • 建立按鈕處理程序
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.
    var fetcher = new NoArgDelegate(
        FetchWeatherFromServer);

    fetcher.BeginInvoke(null, null);
}

當單擊按鈕時,顯示時鐘圖並開始顯示它的動畫效果。 禁用該按鈕, 在一個新線程中調用 FetchWeatherFromServer 方法,而後返回,這樣 Dispatcher 就能夠在咱們等待收集天氣預報時處理事件。網絡

  • 獲取天氣預報
private void FetchWeatherFromServer()
{
    // Simulate the delay from network access.
    Thread.Sleep(4000);

    // Tried and true method for weather forecasting - random numbers.
    var rand = new Random();
    string weather;

    weather = rand.Next(2) == 0 ? "rainy" : "sunny";

    // Schedule the update function in the UI thread.
    tomorrowsWeather.Dispatcher.BeginInvoke(
        DispatcherPriority.Normal,
        new OneArgDelegate(UpdateUserInterface),
        weather);
}

爲簡單起見,此示例中實際沒有任何網絡代碼。 經過使新線程休眠四秒鐘來模擬網絡訪問的延遲。 此時,原始的 UI 線程仍然正在運行並響應事件。爲了對此進行說明,咱們使一個動畫保持運行,並使最小化和最大化按鈕也繼續工做。框架

當延遲結束,而且咱們已隨機選擇了天氣預報時,是時候向 UI 線程返回報告了。爲此,咱們在 UI 線程中使用該線程的 Dispatcher 安排一個對 UpdateUserInterface 的調用。咱們將一個描述天氣的字符串傳遞給安排的此方法調用。dom

  • 更新 UI
private void UpdateUserInterface(string weather)
{
    //Set the weather image
    if (weather == "sunny")
    {
        weatherIndicatorImage.Source = (ImageSource) Resources[
            "SunnyImageSource"];
    }
    else if (weather == "rainy")
    {
        weatherIndicatorImage.Source = (ImageSource) Resources[
            "RainingImageSource"];
    }

    //Stop clock animation
    _showClockFaceStoryboard.Stop(this);
    _hideClockFaceStoryboard.Begin(this);

    //Update UI text
    fetchButton.IsEnabled = true;
    fetchButton.Content = "Fetch Forecast";
    weatherText.Text = weather;
}

當 UI 線程中的 Dispatcher 有時間時,會對 UpdateUserInterface 執行預約調用。此方法中止時鐘動畫並選擇一個圖像來描述天氣。它顯示此圖像並還原「fetch forecast」(獲取預報)按鈕。異步

以上。async

技術細節和難點

  • 使用線程編寫組件

《Microsoft .NET Framework 開發人員指南》介紹了組件向其客戶端公開異步行爲的一種模式(請參見 基於事件的異步模式概述)。例如,假定咱們但願將 FetchWeatherFromServer 方法打包到一個可重用的非圖形組件中。若是採用標準的 Microsoft .NET Framework 模式,那麼代碼應與下面的內容相似。ide

public class WeatherComponent : Component
{
    //gets weather: Asynchronous 
    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 將使用前面介紹的一種技術(如建立後臺線程)來異步執行工做,同時不阻止調用線程。fetch

此模式的最重要部分之一是最初在調用方法名稱 Async 方法的線程上調用方法名稱 Completed 方法。 經過存儲 CurrentDispatcher,您可使用 WPF 輕鬆地實現這一點。可是,以後只能在 WPF應用程序中使用該非圖形組件,而不能在 Windows Forms或 ASP.NET 程序中使用該組件。動畫

DispatcherSynchronizationContext 類可知足這一需求。能夠將該類視爲還使用其餘 UI 框架的 Dispatcher 的簡化版本。

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();
}

此處在MSDN或Microsoft Help查看器中WPF線程處理模型說起。

相關文章
相關標籤/搜索