WPF示例:多線程MultiThreading,BackgroundWorker專線程工做

《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算法

clipboard.png

<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

clipboard.png

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

執行結果:線程

clipboard.png

clipboard.png

以上。code

相關文章
相關標籤/搜索