WPF QuickStart系列之线程模型(Thread Model)
这篇博客将介绍WPF中的线程模型。
首先我们先来看一个例子,用来计算一定范围内的素数个数。
XAML:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" Margin="">
<TextBlock Text="From:" />
<TextBox Margin="10,2,2,2" Width="" MaxLength="" x:Name="_from"/>
<TextBlock Text="To:" Margin="20,0,0,0"/>
<TextBox Margin="10,2,2,2" Width="" MaxLength="" x:Name="_to"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Grid.Row="" Margin="">
<Button Content="Calculate" Padding="" Click="CalcButtonClick" x:Name="_calcButton"/>
<Button Content="Cancel" Padding="" Margin="10,0,0,0" IsEnabled="False" x:Name="_cancelButton" Click="CancleButtonClick"/>
</StackPanel>
<TextBlock x:Name="_result" Grid.Row="" FontSize="" Margin="" HorizontalAlignment="Center" />
</Grid>
C#:
private void CalcButtonClick(object sender, RoutedEventArgs e)
{
_result.Text = string.Empty; int from = int.Parse(_from.Text);
int to = int.Parse(_to.Text); _calcButton.IsEnabled = false; _cancelButton.IsEnabled = true; int total = CountPrimes(from, to); _result.Text = "Total Primes: " + total.ToString(); _calcButton.IsEnabled = true;
} private int CountPrimes(int from, int to)
{
int total = ; for (int i = from; i <= to; i++)
{
bool isPrime = true;
int limit = (int)Math.Sqrt(i);
for (int j = ; j <= limit; j++)
if (i % j == )
{
isPrime = false;
break;
}
if (isPrime)
total++;
}
return total;
}
运行之后,如果输入的值很大,例如需要查找1到100000000之间的素数。发现一会儿界面就会卡死。这是因为我们的计算方法阻塞了UI线程。此时我们应该考虑将计算方法放在一个线程中去计算。将计算的代码放置在ThreadPool中,不建议使用Thread,因为ThreadPool会帮助我们来管理线程。
修改CalcButtonClick的C#代码如下:
private void CalcButtonClick(object sender, RoutedEventArgs e)
{
_result.Text = string.Empty; int from = int.Parse(_from.Text);
int to = int.Parse(_to.Text); _calcButton.IsEnabled = false; _cancelButton.IsEnabled = true; ThreadPool.QueueUserWorkItem((state) => { int total = CountPrimes(from, to); _result.Text = "Total Primes: " + total.ToString(); _calcButton.IsEnabled = true; });
}
此时查找1到100000000之间的素数,发现界面可以自由的拖拽。不过会产生一个异常。截图如下:
错误信息告诉我们在一个线程中访问另一个线程创建的对象。这是因为_result这个TextBlock是UI线程创建的。我们不能在一个后台线程中访问它。下面就要引出WPF中专门用于UI同步的Dispatcher对象。继续改进代码,
private void CalcButtonClick(object sender, RoutedEventArgs e)
{
_result.Text = string.Empty; int from = int.Parse(_from.Text);
int to = int.Parse(_to.Text); _calcButton.IsEnabled = false; _cancelButton.IsEnabled = true; ThreadPool.QueueUserWorkItem((state) => { int total = CountPrimes(from, to); Dispatcher.BeginInvoke(new Action(() =>
{
_result.Text = "Total Primes: " + total.ToString(); _calcButton.IsEnabled = true;
}));
});
}
把更新UI的代码都放置在Dispatcher.BeginInvoke中执行。这次我们会看到最终的执行结果。
细心的你会返现Dispatcher对象还有一个Invoke的方法。这两者的区别BeginInvoke是异步更新UI,不会阻塞UI,Invoke方法是同步更新UI,如果需要更新到UI的内容很多,会造成UI的阻塞,建议使用BeginInvoke。
我们还可以使用SynchronizationContext来进行UI同步,代码如下:
private void CalcButtonClick(object sender, RoutedEventArgs e)
{
_result.Text = string.Empty; int from = int.Parse(_from.Text); int to = int.Parse(_to.Text); Button button = sender as Button; button.IsEnabled = false; SynchronizationContext sc = SynchronizationContext.Current; ThreadPool.QueueUserWorkItem((p) => { int total = CountPrimes(from, to); sc.Send(delegate { _result.Text = "Total Primes: " + total.ToString(); button.IsEnabled = true; }, null);
});
}
SynchronizationContext中有两个方法用来做UI同步,Send和Post,这两者区别于Dispatcher中BeginInvoke和Invoke是一样的。Send方法是异步进行UI同步,Post方法是同步的方式进行UI同步。
说完了WPF中UI线程同步,下面我们看一下如何取消正在进行中的线程操作。
使用CancellationTokenSource类,代码如下:
CancellationTokenSource source = null; public MainWindow()
{
InitializeComponent();
} private void CalcButtonClick(object sender, RoutedEventArgs e)
{
source = new CancellationTokenSource(); _result.Text = string.Empty; int from = int.Parse(_from.Text); int to = int.Parse(_to.Text); Button button = sender as Button; button.IsEnabled = false; SynchronizationContext sc = SynchronizationContext.Current; ThreadPool.QueueUserWorkItem((p) => { int total = CountPrimes(from, to,source.Token); sc.Send(delegate { _result.Text = total > ? "Total Primes: " + total.ToString() : "Canceled."; button.IsEnabled = true; }, null); }); } private int CountPrimes(int from, int to, CancellationToken token)
{
int total = ;
for (int i = from; i <= to; i++)
{
if(token.IsCancellationRequested)
{
return -;
} bool isPrime = true;
int limit = (int)Math.Sqrt(i);
for (int j = ; j <= limit; j++)
if (i % j == )
{
isPrime = false;
break;
}
if (isPrime)
total++;
}
return total;
} private void CancelButtonClick(object sender, RoutedEventArgs e)
{
if(source != null)
{
source.Cancel();
}
}
至此,我们将WPF中后台线程与UI的同步写完了。还有一个重要部分,就是将执行的进度在客户端展示,这样可以让用户清楚的知道目前执行的进度,而不是一直等待,增强了用户体验。可以使用BackgroudWorker来实现。在此就不再赘述。
感谢您的阅读,如果您对博客中内容有疑问,欢迎在评论中指出来。
WPF QuickStart系列之线程模型(Thread Model)的更多相关文章
- WPF QuickStart系列
接触WPF有一段时间了,现在做的项目也是WPF相关的.所以决定写一个WPF QuickStart系列的文章.也是自己对WPF学习的总结,如果对你有帮助,就非常棒了.因为不善言辞,所以尽量以WPF示例和 ...
- 编写高质量代码改善C#程序的157个建议——建议87:区分WPF和WinForm的线程模型
建议87:区分WPF和WinForm的线程模型 WPF和WinForm窗体应用程序都有一个要求,那就是UI元素(如Button.TextBox等)必须由创建它的那个线程进行更新.WinForm在这方面 ...
- 死磕 java线程系列之线程模型
问题 (1)线程类型有哪些? (2)线程模型有哪些? (3)各语言使用的是哪种线程模型? 简介 在Java中,我们平时所说的并发编程.多线程.共享资源等概念都是与线程相关的,这里所说的线程实际上应该叫 ...
- 编写高质量代码改善程序的157个建议:第87个建议之区分WPF和WinForm的线程模型
今天有时间了,继续<编写高质量代码改善程序的157个建议>的阅读,当我阅读到建议87的时候,里面的一些代码示例和文中所说的不一致了,是不是我现在用的是NetFramework 4.0的缘故 ...
- WPF QuickStart系列之样式和模板(Style and Template)
在WPF桌面程序中,当我们想构建一个统一的UI表现时(在不同操作系统下,显示效果一致),此时我们就需要使用到WPF中的样式和模板技术.简单来说,如果我们需要简单的给一个Button设置宽,高,Marg ...
- WPF QuickStart系列之数据绑定(Data Binding)
这篇博客将展示WPF DataBinding的内容. 首先看一下WPF Data Binding的概览, Binding Source可以是任意的CLR对象,或者XML文件等,Binding Targ ...
- WPF QuickStart系列之附加属性(Attached Property)
这一篇博客是关于如何使用附加属性和创建自定义附加属性的. 1. 附加属性使用, WPF中对附加属性使用最多的莫过于对控件布局时设置控件的位置,例如在Canvas中有一个Rectangle, Ellip ...
- 「译」JUnit 5 系列:扩展模型(Extension Model)
原文地址:http://blog.codefx.org/design/architecture/junit-5-extension-model/ 原文日期:11, Apr, 2016 译文首发:Lin ...
- posix 线程(一):线程模型、pthread 系列函数 和 简单多线程服务器端程序
posix 线程(一):线程模型.pthread 系列函数 和 简单多线程服务器端程序 一.线程有3种模型,分别是N:1用户线程模型,1:1核心线程模型和N:M混合线程模型,posix thread属 ...
随机推荐
- treeiso
主要包括了一些树同构的代码和一些树图生成器... download
- 【leetcode】Populating Next Right Pointers in Each Node II
Populating Next Right Pointers in Each Node II Follow up for problem "Populating Next Right Poi ...
- POJ 1917
http://poj.org/problem?id=1917 poj的字符串的一道水题. 题意么无关紧要, 反正输出的第一行就是把那个<>去掉,s1<s2>s3<s4&g ...
- TcxComboBox控件说明
http://www.cnblogs.com/huangygdelphi/articles/2648490.html 属性: Text:ComboBox 的文本信息 EditText: 也是给Comb ...
- ffmpeg-20160526-git-bin
ESC 退出 0 进度条开关 1 屏幕原始大小 2 屏幕1/2大小 3 屏幕1/3大小 4 屏幕1/4大小 S 下一帧 [ -2秒 ] +2秒 ; -1秒 ' +1秒 下一个帧 -> -5秒 f ...
- [转] Android Volley完全解析(一),初识Volley的基本用法
版权声明:本文出自郭霖的博客,转载必须注明出处. 目录(?)[-] Volley简介 下载Volley StringRequest的用法 JsonRequest的用法 转载请注明出处:http ...
- MST:Agri-Net(POJ 1258)
Agri-Net 题目大意:农夫有一片农场,现在他要把这些田地用管子连起来,田地之间有一定距离,铺设每一段管子的长度与这些田地与田地距离是一样的,问你最小的铺设方案. 这一题很裸,Kruskal算法 ...
- codeforces 558B. Amr and The Large Array 解题报告
题目链接:http://codeforces.com/problemset/problem/558/B 题目意思:给出一个序列,然后找出出现次数最多,但区间占用长度最短的区间左右值. 由于是边读入边比 ...
- JQuery 鼠标事件简介
mouseover事件于用户把鼠标从一个元素移动到另外一个元素上时触发,mouseout事件于用户把鼠标移出一个元素时触发. 下面为你详细介绍下jquery中的鼠标事件: (1):click事件:cl ...
- JAVA导出pdf实例
一.直接导出成PDF Java代码 1. import java.io.FileNotFoundException; 2. import java.io.FileOutputStream; 3. ...