编写高质量代码改善程序的157个建议:第87个建议之区分WPF和WinForm的线程模型
今天有时间了,继续《编写高质量代码改善程序的157个建议》的阅读,当我阅读到建议87的时候,里面的一些代码示例和文中所说的不一致了,是不是我现在用的是NetFramework 4.0的缘故,已经把一些问题修复了,今天把问题写下来,告诉大家文中有些小问题需要修复一下。
WPF和WinForm窗体应用程序都有一个要求,那就是UI元素(Button,Label,Textbox控件等)必须由创建它的那个线程来更新。WinForm这方面的限制并不是很严格,所以像下面这样的代码,在Winform中的大部分情况下都可以运行:
- private void buttonStartAsync_Click(object sender,EventArgs e)
- {
- Task t=new Task(()=>{
- while(true)
- {
- label1.Text=DateTime.Now.ToString();
- Thread.Sleep();
- }
- });
- t.ContinueWith((task)=>{
- try
- {
- task.Wait();
- }
- catch(AggregateException ex)
- {
- Foreach(Exception item in ex.InnerExceptions)
- {
- MessageBox.Show(string.Format("异常类型:{0}{1}来自:{2}{3}异常内容:{4}",item.GetType(),Environment.NewLine,item.Source,Environment.NewLine,item.Message));
- }
- }
- },TaskContinuationOptions.OnlyOnFauled);
- t.Start();
- }
我把这段代码原封不动的有敲了一遍,但是在我的测试实例里面抛出了异常,截图如下:
现在在Winform里面,我测试过的Task和多线程的操作都会报这个错误,修正这个错误很容易,可以在当前类的构造函数里面增加一下一段代码就可以:
- CheckForIllegalCrossThreadCalls = false;
代码效果截图如下:
现在就好了,程序就可以正常运行了,我的文章【其他信息: 线程间操作无效: 从不是创建控件“控件名”的线程访问它。】可以解决这类问题,有详细解释。
所以说,WPF和WinForm都是严格执行主线程操作UI元素的原则。
处理多线程情况下访问UI控件还有很多方法,现在我就在罗列出一下代码:
- Task t = new Task(()=> {
- 2 while (true)
- {
- if (lblResult.InvokeRequired)
- {
- lblResult.BeginInvoke(new Action(() =>
- {
- lblResult.Text = DateTime.Now.ToString();
- }));
- }
- else
- {
- lblResult.Text = DateTime.Now.ToString();
- }
- Thread.Sleep();
- }
- });
- t.ContinueWith((task)=> {
- try
- {
- task.Wait();
- }
- catch (AggregateException ex)
- {
- foreach (var item in ex.InnerExceptions)
- {
- MessageBox.Show(string.Format("异常类型:{0}{1}来自:{2}{3}异常内容:{4}",item.GetType(),Environment.NewLine,item.Source,Environment.NewLine,item.Message));
- }
- }
- },TaskContinuationOptions.OnlyOnFaulted);
- t.Start();
我们可以模仿WPF处理线程的方法,增加两个新方法,这两个方法是CheckAccess和VerifyAccess,这两个方法是WPF的UI控件的最终积累DispatcherObject类型中的两个方法,代码如下:
- namespace System.Windows.Threading
- {
- //
- // 摘要:
- // 表示与 System.Windows.Threading.Dispatcher 关联的对象。
- public abstract class DispatcherObject
- {
- //
- // 摘要:
- // 初始化 System.Windows.Threading.DispatcherObject 类的新实例。
- protected DispatcherObject();
- //
- // 摘要:
- // 获取与此 System.Windows.Threading.DispatcherObject 关联的 System.Windows.Threading.Dispatcher。
- //
- // 返回结果:
- // 调度程序。
- [EditorBrowsable(EditorBrowsableState.Advanced)]
- public Dispatcher Dispatcher { get; }
- //
- // 摘要:
- // 确定调用线程是否可以访问此 System.Windows.Threading.DispatcherObject。
- //
- // 返回结果:
- // 如果调用线程可以访问此对象,则为 true;否则,为 false。
- [EditorBrowsable(EditorBrowsableState.Never)]
- public bool CheckAccess();
- //
- // 摘要:
- // 强制调用线程具有此 System.Windows.Threading.DispatcherObject 的访问权限。
- //
- // 异常:
- // T:System.InvalidOperationException:
- // 调用线程不可以访问此 System.Windows.Threading.DispatcherObject。
- [EditorBrowsable(EditorBrowsableState.Never)]
- public void VerifyAccess();
- }
- }
然后,我们给自己的类型加两个类似的方法,完整代码如下:
- public partial class Form1 : Form
- {
- private Thread mainThread;
- public Form1()
- {
- InitializeComponent();
- }
- bool CheckAccess()
- {
- return mainThread == Thread.CurrentThread;
- }
- void VerifyAccess()
- {
- if (!CheckAccess())
- {
- throw new InvalidOperationException("调用线程无法访问对象,因为另一个线程拥有此对象!");
- }
- }
- private void button1_Click(object sender, EventArgs e)
- {
- Task t = new Task(()=> {
- while (true)
- {
- if (!CheckAccess())
- {
- lblResult.BeginInvoke(new Action(() =>
- {
- lblResult.Text = DateTime.Now.ToString();
- }));
- }
- else
- {
- lblResult.Text = DateTime.Now.ToString();
- }
- Thread.Sleep();
- }
- });
- t.ContinueWith((task)=> {
- try
- {
- task.Wait();
- }
- catch (AggregateException ex)
- {
- foreach (var item in ex.InnerExceptions)
- {
- MessageBox.Show(string.Format("异常类型:{0}{1}来自:{2}{3}异常内容:{4}",item.GetType(),Environment.NewLine,item.Source,Environment.NewLine,item.Message));
- }
- }
- },TaskContinuationOptions.OnlyOnFaulted);
- t.Start();
- }
- }
多线程是一个很复杂的话题,我也在学习阶段和总结阶段,有不足的地方,大家多多指教。
编写高质量代码改善程序的157个建议:第87个建议之区分WPF和WinForm的线程模型的更多相关文章
- 编写高质量代码改善程序的157个建议:使用Dynamic来简化反射的实现
最近有时间看点书了,把157个建议在重新看一遍,代码都调试一遍.当我看到第15个建议的时候有些出入,就记录下来,欢迎大家来探讨. 第十五条建议是,使用dynamic简化反射的使用,没有说明具体的条件. ...
- Java编写高质量代码改善程序的151个建议
第一章 Java开发中通用的方法和准则 建议1:不要在常量和变量中出现易混淆的字母: (i.l.1:o.0等). 建议2:莫让常量蜕变成变量: (代码运行工程中不要改变常量值). 建议3:三元操作符 ...
- 编写高质量代码改善C#程序的157个建议[1-3]
原文:编写高质量代码改善C#程序的157个建议[1-3] 前言 本文主要来学习记录前三个建议. 建议1.正确操作字符串 建议2.使用默认转型方法 建议3.区别对待强制转换与as和is 其中有很多需要理 ...
- 读书--编写高质量代码 改善C#程序的157个建议
最近读了陆敏技写的一本书<<编写高质量代码 改善C#程序的157个建议>>书写的很好.我还看了他的博客http://www.cnblogs.com/luminji . 前面部 ...
- 编写高质量代码改善C#程序的157个建议——建议157:从写第一个界面开始,就进行自动化测试
建议157:从写第一个界面开始,就进行自动化测试 如果说单元测试是白盒测试,那么自动化测试就是黑盒测试.黑盒测试要求捕捉界面上的控件句柄,并对其进行编码,以达到模拟人工操作的目的.具体的自动化测试请学 ...
- 编写高质量代码改善C#程序的157个建议——建议156:利用特性为应用程序提供多个版本
建议156:利用特性为应用程序提供多个版本 基于如下理由,需要为应用程序提供多个版本: 应用程序有体验版和完整功能版. 应用程序在迭代过程中需要屏蔽一些不成熟的功能. 假设我们的应用程序共有两类功能: ...
- 编写高质量代码改善C#程序的157个建议——建议155:随生产代码一起提交单元测试代码
建议155:随生产代码一起提交单元测试代码 首先提出一个问题:我们害怕修改代码吗?是否曾经无数次面对乱糟糟的代码,下决心进行重构,然后在一个月后的某个周一,却收到来自测试版的报告:新的版本,没有之前的 ...
- 编写高质量代码改善C#程序的157个建议——建议154:不要过度设计,在敏捷中体会重构的乐趣
建议154:不要过度设计,在敏捷中体会重构的乐趣 有时候,我们不得不随时更改软件的设计: 如果项目是针对某个大型机构的,不同级别的软件使用者,会提出不同的需求,或者随着关键岗位人员的更替,需求也会随个 ...
- 编写高质量代码改善C#程序的157个建议——建议153:若抛出异常,则必须要注释
建议153:若抛出异常,则必须要注释 有一种必须加注释的场景,即使异常.如果API抛出异常,则必须给出注释.调用者必须通过注释才能知道如何处理那些专有的异常.通常,即便良好的命名也不可能告诉我们方法会 ...
随机推荐
- Linux 下按时间顺序批量删除文件
ls -lrt| awk '{print $9}'| head -n 10 | xargs rm -rf 1.文件按时间排序: 2.获取文件名字: 3.取前10个文件 4.删除文件
- python 之变量
什么是变量? 变量就是存储一个不固定的值,可以随时更改其值. 1.变量不仅可以是数字,还可以是任意数据类型 2.变量名必须是大小写英文.数字和_的组合,且不能用数字开头 python变量如何存储 首先 ...
- Cassandra Issue with Tombstone
1. Cassandra is quicker than postgre and have lower change to lose data. Cassandra doesn't have fore ...
- 无锁atomicInteger
AtomicInteger可以保证硬件上的原子操作 1.主要原理 CAS操作 在进行数据更新的时候,会进行与内存中的地址进行比较,若预期值与内存中的值相同,则进行数据上的更新,若值不同,则更新失败, ...
- ios runtime 打印内 内部调用的属性
unsigned int count = 0; // 拷贝出所有的成员变量列表 Ivar *ivars = class_copyIvarList([UITextField class], &c ...
- Web缓存相关知识整理
一.前言 工作上遇到一个这样的需求,一个H5页面在APP端,如果勾选已读状态,则下次打开该链接,会跳过此页面.用到了HTML5 的本地存储 API 中的 localStorage作为解决方案,回顾了 ...
- Eclipse导入项目常见问题----服务器版本问题02
*有时导入项目时,我们打开build时会看到服务器包上有一个红色的×,和这里JRE System jar包显示的是一个样的,左侧有个红色叉: 解决办法 打开到如下界面,解决步骤如下图: 打开该界面方式 ...
- 38. Count and Say - Unsolved
https://leetcode.com/problems/count-and-say/#/description The count-and-say sequence is the sequence ...
- Hadoop集群搭建(非HA)
1.准备Linux环境 1.0先将虚拟机的网络模式选为NAT 1.1修改主机名 vi /etc/sysconfig/network NETWORKING=yes HOSTNAME=itcast ### ...
- 警告: [SetContextPropertiesRule]{Context} Setting property 'source' to 'org.eclipse.jst.jee.server:JsonBlog' did not find a matching property.
这个问题困扰很久了,逛了很多论坛,终于得以解决 我的控制台错误如下: 五月 , :: 下午 org.apache.catalina.startup.VersionLoggerListener log ...