今天有时间了,继续《编写高质量代码改善程序的157个建议》的阅读,当我阅读到建议87的时候,里面的一些代码示例和文中所说的不一致了,是不是我现在用的是NetFramework 4.0的缘故,已经把一些问题修复了,今天把问题写下来,告诉大家文中有些小问题需要修复一下。

WPF和WinForm窗体应用程序都有一个要求,那就是UI元素(Button,Label,Textbox控件等)必须由创建它的那个线程来更新。WinForm这方面的限制并不是很严格,所以像下面这样的代码,在Winform中的大部分情况下都可以运行:

  1. private void buttonStartAsync_Click(object sender,EventArgs e)
  2. {
  3. Task t=new Task(()=>{
  4. while(true)
  5. {
  6. label1.Text=DateTime.Now.ToString();
  7. Thread.Sleep();
  8. }
  9. });
  10. t.ContinueWith((task)=>{
  11. try
  12. {
  13. task.Wait();
  14. }
  15. catch(AggregateException ex)
  16. {
  17. Foreach(Exception item in ex.InnerExceptions)
  18. {
  19. MessageBox.Show(string.Format("异常类型:{0}{1}来自:{2}{3}异常内容:{4}",item.GetType(),Environment.NewLine,item.Source,Environment.NewLine,item.Message));
  20. }
  21. }
  22. },TaskContinuationOptions.OnlyOnFauled);
  23. t.Start();
  24. }

我把这段代码原封不动的有敲了一遍,但是在我的测试实例里面抛出了异常,截图如下:

现在在Winform里面,我测试过的Task和多线程的操作都会报这个错误,修正这个错误很容易,可以在当前类的构造函数里面增加一下一段代码就可以:

  1. CheckForIllegalCrossThreadCalls = false;

代码效果截图如下:

现在就好了,程序就可以正常运行了,我的文章【其他信息: 线程间操作无效: 从不是创建控件“控件名”的线程访问它。】可以解决这类问题,有详细解释。

所以说,WPF和WinForm都是严格执行主线程操作UI元素的原则。

处理多线程情况下访问UI控件还有很多方法,现在我就在罗列出一下代码:

  1. Task t = new Task(()=> {
  2. 2 while (true)
  3. {
  4. if (lblResult.InvokeRequired)
  5. {
  6. lblResult.BeginInvoke(new Action(() =>
  7. {
  8. lblResult.Text = DateTime.Now.ToString();
  9. }));
  10. }
  11. else
  12. {
  13. lblResult.Text = DateTime.Now.ToString();
  14. }
  15. Thread.Sleep();
  16. }
  17. });
  18. t.ContinueWith((task)=> {
  19. try
  20. {
  21. task.Wait();
  22. }
  23. catch (AggregateException ex)
  24. {
  25. foreach (var item in ex.InnerExceptions)
  26. {
  27. MessageBox.Show(string.Format("异常类型:{0}{1}来自:{2}{3}异常内容:{4}",item.GetType(),Environment.NewLine,item.Source,Environment.NewLine,item.Message));
  28. }
  29. }
  30. },TaskContinuationOptions.OnlyOnFaulted);
  31. t.Start();

我们可以模仿WPF处理线程的方法,增加两个新方法,这两个方法是CheckAccess和VerifyAccess,这两个方法是WPF的UI控件的最终积累DispatcherObject类型中的两个方法,代码如下:

  1. namespace System.Windows.Threading
  2. {
  3. //
  4. // 摘要:
  5. // 表示与 System.Windows.Threading.Dispatcher 关联的对象。
  6. public abstract class DispatcherObject
  7. {
  8. //
  9. // 摘要:
  10. // 初始化 System.Windows.Threading.DispatcherObject 类的新实例。
  11. protected DispatcherObject();
  12.  
  13. //
  14. // 摘要:
  15. // 获取与此 System.Windows.Threading.DispatcherObject 关联的 System.Windows.Threading.Dispatcher。
  16. //
  17. // 返回结果:
  18. // 调度程序。
  19. [EditorBrowsable(EditorBrowsableState.Advanced)]
  20. public Dispatcher Dispatcher { get; }
  21.  
  22. //
  23. // 摘要:
  24. // 确定调用线程是否可以访问此 System.Windows.Threading.DispatcherObject。
  25. //
  26. // 返回结果:
  27. // 如果调用线程可以访问此对象,则为 true;否则,为 false。
  28. [EditorBrowsable(EditorBrowsableState.Never)]
  29. public bool CheckAccess();
  30. //
  31. // 摘要:
  32. // 强制调用线程具有此 System.Windows.Threading.DispatcherObject 的访问权限。
  33. //
  34. // 异常:
  35. // T:System.InvalidOperationException:
  36. // 调用线程不可以访问此 System.Windows.Threading.DispatcherObject。
  37. [EditorBrowsable(EditorBrowsableState.Never)]
  38. public void VerifyAccess();
  39. }
  40. }

然后,我们给自己的类型加两个类似的方法,完整代码如下:

  1. public partial class Form1 : Form
  2. {
  3. private Thread mainThread;
  4. public Form1()
  5. {
  6. InitializeComponent();
  7. }
  8.  
  9. bool CheckAccess()
  10. {
  11. return mainThread == Thread.CurrentThread;
  12. }
  13.  
  14. void VerifyAccess()
  15. {
  16. if (!CheckAccess())
  17. {
  18. throw new InvalidOperationException("调用线程无法访问对象,因为另一个线程拥有此对象!");
  19. }
  20. }
  21. private void button1_Click(object sender, EventArgs e)
  22. {
  23. Task t = new Task(()=> {
  24. while (true)
  25. {
  26. if (!CheckAccess())
  27. {
  28. lblResult.BeginInvoke(new Action(() =>
  29. {
  30. lblResult.Text = DateTime.Now.ToString();
  31. }));
  32. }
  33. else
  34. {
  35. lblResult.Text = DateTime.Now.ToString();
  36. }
  37. Thread.Sleep();
  38. }
  39. });
  40. t.ContinueWith((task)=> {
  41. try
  42. {
  43. task.Wait();
  44. }
  45. catch (AggregateException ex)
  46. {
  47. foreach (var item in ex.InnerExceptions)
  48. {
  49. MessageBox.Show(string.Format("异常类型:{0}{1}来自:{2}{3}异常内容:{4}",item.GetType(),Environment.NewLine,item.Source,Environment.NewLine,item.Message));
  50. }
  51. }
  52. },TaskContinuationOptions.OnlyOnFaulted);
  53. t.Start();
  54. }
  55. }

多线程是一个很复杂的话题,我也在学习阶段和总结阶段,有不足的地方,大家多多指教。

编写高质量代码改善程序的157个建议:第87个建议之区分WPF和WinForm的线程模型的更多相关文章

  1. 编写高质量代码改善程序的157个建议:使用Dynamic来简化反射的实现

    最近有时间看点书了,把157个建议在重新看一遍,代码都调试一遍.当我看到第15个建议的时候有些出入,就记录下来,欢迎大家来探讨. 第十五条建议是,使用dynamic简化反射的使用,没有说明具体的条件. ...

  2. Java编写高质量代码改善程序的151个建议

    第一章  Java开发中通用的方法和准则 建议1:不要在常量和变量中出现易混淆的字母: (i.l.1:o.0等). 建议2:莫让常量蜕变成变量: (代码运行工程中不要改变常量值). 建议3:三元操作符 ...

  3. 编写高质量代码改善C#程序的157个建议[1-3]

    原文:编写高质量代码改善C#程序的157个建议[1-3] 前言 本文主要来学习记录前三个建议. 建议1.正确操作字符串 建议2.使用默认转型方法 建议3.区别对待强制转换与as和is 其中有很多需要理 ...

  4. 读书--编写高质量代码 改善C#程序的157个建议

    最近读了陆敏技写的一本书<<编写高质量代码  改善C#程序的157个建议>>书写的很好.我还看了他的博客http://www.cnblogs.com/luminji . 前面部 ...

  5. 编写高质量代码改善C#程序的157个建议——建议157:从写第一个界面开始,就进行自动化测试

    建议157:从写第一个界面开始,就进行自动化测试 如果说单元测试是白盒测试,那么自动化测试就是黑盒测试.黑盒测试要求捕捉界面上的控件句柄,并对其进行编码,以达到模拟人工操作的目的.具体的自动化测试请学 ...

  6. 编写高质量代码改善C#程序的157个建议——建议156:利用特性为应用程序提供多个版本

    建议156:利用特性为应用程序提供多个版本 基于如下理由,需要为应用程序提供多个版本: 应用程序有体验版和完整功能版. 应用程序在迭代过程中需要屏蔽一些不成熟的功能. 假设我们的应用程序共有两类功能: ...

  7. 编写高质量代码改善C#程序的157个建议——建议155:随生产代码一起提交单元测试代码

    建议155:随生产代码一起提交单元测试代码 首先提出一个问题:我们害怕修改代码吗?是否曾经无数次面对乱糟糟的代码,下决心进行重构,然后在一个月后的某个周一,却收到来自测试版的报告:新的版本,没有之前的 ...

  8. 编写高质量代码改善C#程序的157个建议——建议154:不要过度设计,在敏捷中体会重构的乐趣

    建议154:不要过度设计,在敏捷中体会重构的乐趣 有时候,我们不得不随时更改软件的设计: 如果项目是针对某个大型机构的,不同级别的软件使用者,会提出不同的需求,或者随着关键岗位人员的更替,需求也会随个 ...

  9. 编写高质量代码改善C#程序的157个建议——建议153:若抛出异常,则必须要注释

    建议153:若抛出异常,则必须要注释 有一种必须加注释的场景,即使异常.如果API抛出异常,则必须给出注释.调用者必须通过注释才能知道如何处理那些专有的异常.通常,即便良好的命名也不可能告诉我们方法会 ...

随机推荐

  1. Linux 下按时间顺序批量删除文件

    ls -lrt| awk '{print $9}'| head -n 10 | xargs rm -rf 1.文件按时间排序: 2.获取文件名字: 3.取前10个文件 4.删除文件

  2. python 之变量

    什么是变量? 变量就是存储一个不固定的值,可以随时更改其值. 1.变量不仅可以是数字,还可以是任意数据类型 2.变量名必须是大小写英文.数字和_的组合,且不能用数字开头 python变量如何存储 首先 ...

  3. Cassandra Issue with Tombstone

    1. Cassandra is quicker than postgre and have lower change to lose data. Cassandra doesn't have fore ...

  4. 无锁atomicInteger

    AtomicInteger可以保证硬件上的原子操作 1.主要原理 CAS操作 在进行数据更新的时候,会进行与内存中的地址进行比较,若预期值与内存中的值相同,则进行数据上的更新,若值不同,则更新失败,  ...

  5. ios runtime 打印内 内部调用的属性

    unsigned int count = 0; // 拷贝出所有的成员变量列表 Ivar *ivars = class_copyIvarList([UITextField class], &c ...

  6. Web缓存相关知识整理

    一.前言  工作上遇到一个这样的需求,一个H5页面在APP端,如果勾选已读状态,则下次打开该链接,会跳过此页面.用到了HTML5 的本地存储 API 中的 localStorage作为解决方案,回顾了 ...

  7. Eclipse导入项目常见问题----服务器版本问题02

    *有时导入项目时,我们打开build时会看到服务器包上有一个红色的×,和这里JRE System jar包显示的是一个样的,左侧有个红色叉: 解决办法 打开到如下界面,解决步骤如下图: 打开该界面方式 ...

  8. 38. Count and Say - Unsolved

    https://leetcode.com/problems/count-and-say/#/description The count-and-say sequence is the sequence ...

  9. Hadoop集群搭建(非HA)

    1.准备Linux环境 1.0先将虚拟机的网络模式选为NAT 1.1修改主机名 vi /etc/sysconfig/network NETWORKING=yes HOSTNAME=itcast ### ...

  10. 警告: [SetContextPropertiesRule]{Context} Setting property 'source' to 'org.eclipse.jst.jee.server:JsonBlog' did not find a matching property.

    这个问题困扰很久了,逛了很多论坛,终于得以解决 我的控制台错误如下: 五月 , :: 下午 org.apache.catalina.startup.VersionLoggerListener log ...