今天有时间了,继续《编写高质量代码改善程序的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的线程模型的更多相关文章

  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. Spring Boot 整合 Elasticsearch,实现 function score query 权重分查询

    摘要: 原创出处 www.bysocket.com 「泥瓦匠BYSocket 」欢迎转载,保留摘要,谢谢! 『 预见未来最好的方式就是亲手创造未来 – <史蒂夫·乔布斯传> 』 运行环境: ...

  2. NioSocket相关知识

    一.Nio简介 nio 是non-blocking的简称,在jdk1.4 里提供的新api .Sun 官方标榜的特性如下: 为所有的原始类型提供(Buffer)缓存支持.字符集编码解码解决方案. Ch ...

  3. iOS自动打包并发布脚本

    假如你的项目目录如下所示: |____AOP | |____AppDelegate.h | |____AppDelegate.m | |____Base.lproj | | |____LaunchSc ...

  4. Linux中重定向

    转:http://blog.csdn.net/songyang516/article/details/6758256 1重定向 1.1      重定向符号 >               输出 ...

  5. 调整分区大小(ext2\ext3\ext4)

    现在的时间是2017年5月27日 我想说调整分区大小没那么麻烦,至少我直接将一个ext3格式分区从50G减少到了30G. 步骤如下 1 首先确保那个分区是没被挂载的. 2 调整分区大小,但是其实并没有 ...

  6. Sampling Distributions and Central Limit Theorem in R(转)

    The Central Limit Theorem (CLT), and the concept of the sampling distribution, are critical for unde ...

  7. Spring学习笔记——01 控制反转

    想一下之前学的Java,如果某个类需要引用某个对象,则需要手动new一个出来.这样带来的一个问题就是,若被引用的类发生改动或被删除,则引用它的所有类都会报错.因为两个类耦合在一起了.解决的办法就是不由 ...

  8. collections模块方法详解

    collections是Python内建的一个集合模块,提供了许多有用的集合类. namedtuple 我们知道tuple可以表示不变集合,例如,一个点的二维坐标就可以表示成: >>> ...

  9. Python教程(0)——介绍

    What's Python? Python是一种编程语言,由荷兰人Guido van Rossum于1989年,为打发无聊的圣诞节而开发的. Python的优点是语法简洁,代码量比C/C++.Java ...

  10. 用hmmlearn学习隐马尔科夫模型HMM

    在之前的HMM系列中,我们对隐马尔科夫模型HMM的原理以及三个问题的求解方法做了总结.本文我们就从实践的角度用Python的hmmlearn库来学习HMM的使用.关于hmmlearn的更多资料在官方文 ...