今天有时间了,继续《编写高质量代码改善程序的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. 第一篇:使用Spark探索经典数据集MovieLens

    前言 MovieLens数据集包含多个用户对多部电影的评级数据,也包括电影元数据信息和用户属性信息. 这个数据集经常用来做推荐系统,机器学习算法的测试数据集.尤其在推荐系统领域,很多著名论文都是基于这 ...

  2. Opencv在linux下安装

    Opencv in Linux These steps have been tested for Ubuntu 10.04 but should work with other distros as ...

  3. MSICE界面和功能分析

    一.首页 ICE实现的这种界面样式,有可能使用WCF实现的,但是MFC来模仿也是可行的. 包括配置界面,和右下角的细节. 首页的主要功能只有3个,分别为图片拼接.视频拼接和打开拼接文件. 二.输入拼接 ...

  4. rpm不用yum安装rabbitMQ

    1.安装erlang 下载 esl-erlang_19.0~centos~6_amd64.rpm 打开https://www.erlang-solutions.com/resources/downlo ...

  5. 从SonarQube谈设计模式

    SonarQube SonarQube是用来检测代码质量的,但类似工具的推广常常遇到阻碍. 成型项目或僵尸项目可以理解,项目优化需要投入的人力成本和时间成本太大,而且最主要的是无法保证改动过程中不引入 ...

  6. JAVA设计模式初探之装饰者模式

    定义:动态给一个对象添加一些额外的职责,就象在墙上刷油漆.使用Decorator模式相比用生成子类方式达到功能的扩充显得更为灵活.设计初衷:通常可以使用继承来实现功能的拓展,如果这些需要拓展的功能的种 ...

  7. Android Studio的两种模式及签名配置

    我们使用Android Studio 运行我们的app,无非两种模式:debug和release模式. debug模式 debug模式使用一个默认的debug.keystore进行签名. 这个默认签名 ...

  8. 掌握Chrome Developer Tools:下一阶段前端开发技术

    Tips 原文作者:Ben Edelstein 原文地址:Mastering Chrome Developer Tools: Next Level Front-End Development Tech ...

  9. iOS的内存分析和内存管理

    iOS的内存分析和内存管理 [内存管理]一直是iOS开发中的一个重点. 本文就带你从内存分析开始一步步了解内存的占用情况,从真实的情况中领悟真正项目开发过程中的内存的使用情况. 注:本文默认你熟悉 M ...

  10. WPF MVVM 架构 Step By Step(3)(把后台代码移到一个类中)

    我觉得大部分开发者应该已经知道怎么去解决这个问题.一般都是把后台代码(GLUE code)移动到一个类库.这个类库用来代表UI的属性和行为.任何代码当被移到一个类库中时都可以被编译成一个DLL,然后可 ...