WPF线程详解(五)——BeginInvoke

本文将通过一个WPF模拟程序来演示一下界面卡死的现象,并通过修改代码来解决界面卡死的问题。

希望通过对本文的学习,大家能对Dispatcher.BeginInvoke()方法有一个新的认识。

文章开篇直接给出界面卡死的示例代码。

示例WPF程序,用来计算1~n的和值,这里的n可以是1亿~25 亿之间的某个值,通过界面录入,结果显示在n输入框后面的文本框中,既然是WPF程序,代码包含xamlcs代码两部分,本文一并给出。

以下为cs代码:

[csharp]  view plain  copy
  1. using System;  
  2. using System.Windows;  
  3. using System.Threading;  
  4.   
  5. namespace DispatcherExample  
  6. {  
  7.     /// <summary>  
  8.     /// MainWindow.xaml 的交互逻辑  
  9.     /// </summary>  
  10.     public partial class MainWindow : Window  
  11.     {  
  12.         public MainWindow()  
  13.         {  
  14.             InitializeComponent();  
  15.         }  
  16.   
  17.         private void button1_Click(object sender, RoutedEventArgs e)  
  18.         {  
  19.             Int64 inputNumber;  
  20.             if (!Int64.TryParse(this.textBox1.Text, out inputNumber))  
  21.             {  
  22.                 MessageBox.Show("请输入1亿-10亿皑间的整型数据!");  
  23.                 return;  
  24.             }  
  25.             if (inputNumber > 2500000000 || inputNumber<100000000)  
  26.             {  
  27.                 MessageBox.Show("请输入1亿-10亿间的整型数据!");  
  28.                 return;  
  29.             }  
  30.             Thread newThread = new Thread(new ParameterizedThreadStart(GetResult));  
  31.             newThread.Start(inputNumber);  
  32.         }  
  33.   
  34.         private void GetResult(object inputNumber)  
  35.         {  
  36.             this.Dispatcher.BeginInvoke((Action)delegate()  
  37.             {  
  38.                 this.textBox2.Text = CalcSum((Int64)inputNumber).ToString();  
  39.             });  
  40.         }  
  41.   
  42.         private double CalcSum(Int64 inputNumber)  
  43.         {  
  44.             double sum=0;  
  45.             for (int i = 0; i < inputNumber; i++)  
  46.             {  
  47.                 sum +=i;  
  48.             }  
  49.             return sum;  
  50.         }  
  51.     }  
  52. }  

以下为xaml代码:

[html]  view plain  copy
  1. <Window x:Class="DispatcherExample.MainWindow"  
  2.         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
  3.         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
  4.         Title="求(和)你亿万次~~" Height="350" Width="525" ResizeMode="NoResize">  
  5.     <Grid>  
  6.         <Grid.ColumnDefinitions>  
  7.             <ColumnDefinition Width="252*" />  
  8.             <ColumnDefinition Width="251*" />  
  9.         </Grid.ColumnDefinitions>  
  10.         <Button Content="计算和值" Height="23" HorizontalAlignment="Left" Margin="213,168,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" Grid.ColumnSpan="2" />  
  11.         <Label Content="输入1亿-25亿间的数字:" Height="28" HorizontalAlignment="Left" Margin="36,93,0,0" Name="label1" VerticalAlignment="Top" />  
  12.         <TextBox Height="23" HorizontalAlignment="Left" Margin="158,96,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" Grid.ColumnSpan="2" />  
  13.         <TextBox Height="23" HorizontalAlignment="Left" Margin="35,96,0,0" Name="textBox2" VerticalAlignment="Top" Width="177" Text="结果看这里..." Grid.Column="1" />  
  14.     </Grid>  
  15. </Window>  

执行程序,界面如下:

输入2500000000,点击“计算和值”按钮,程序开始计算和值,界面卡死,无法再操作该程序(如移动位置或重新输入等)。

分析代码,发现问题应该出在下面的代码中,因为该部分代码中存在调用UI主线程的操作,此种操作不当往往会导致界面卡死的现象。

[csharp]  view plain  copy
  1. private void GetResult(object inputNumber)  
  2. {  
  3.      this.Dispatcher.BeginInvoke((Action)delegate()  
  4.      {  
  5.            this.textBox2.Text = CalcSum((Int64)inputNumber).ToString();  
  6.      });  
  7. }  


那么,问题到底出在哪里呢?

要想弄清楚这点,还得了解一下Dispatcher.BeginInvoke()方法。

MSDN上对Dispatcher.BeginInvoke方法的解释如下

Dispatcher.BeginInvoke 方法 (Action)

在与 Dispatcher关联的线程上异步执行指定的委托。 

那么本实例中,与 Dispatcher关联的线程是什么呢?

要想弄清楚这点很简单。只要知道this.Dispatcher.BeginInvoke()中的this指的是什么就可以了。在Visual studio中将鼠标至于this上,发现this指的是当前的窗体类(如下图),即程序的主线程。

到这,我们应该知道问题出在哪里了。

原因是:在GetResult()方法中,将求和的操作交由主线程来完成,当计算未完成时,界面自然会被卡死。

通过与同事交谈了解到,他其实想要的是:新开一个线程来完成自己预想的运算(类似于示例程序中的求和运算),在结果出来后再调用主线程显示结果。

这样界面就不会出现卡死现象,但是上面的代码并没有达到预想结果。

原因前面已经交代了,因为这段代码将求和的计算仍然丢给了主线程,尽管新开了线程,但是新开线程并不进行求和运算,可以说是绕了一圈又回来了。

主线程开新线程,新线程又调用主线程。这有点像工作中的踢皮球,我给你一件事,你说不会,又踢回给我。

找到原因再修改就简单了,修改后的代码如下:

[csharp]  view plain  copy
  1. private void GetResult(object inputNumber)  
  2. {  
  3.      double result=CalcSum((Int64)inputNumber);  
  4.      this.Dispatcher.BeginInvoke((Action)delegate()  
  5.      {  
  6.            //this.textBox2.Text = CalcSum((Int64)inputNumber).ToString();  
  7.            this.textBox2.Text = result.ToString();            
  8.      });  
  9. }  

至于为什么要这样修改,我想:你懂的。

再次执行程序,输入2500000000,求和,界面不再存在卡死现象。

猜你喜欢

转载自blog.csdn.net/yinweimumu/article/details/80582863