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

时间:2022-09-14 09:03:15

今天有时间了,继续《编写高质量代码改善程序的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();
}

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

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

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

CheckForIllegalCrossThreadCalls = false;

代码效果截图如下:

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

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

所以说,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&num;程序的157个建议——建议157:从写第一个界面开始,就进行自动化测试

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

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

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

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

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

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

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

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

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

随机推荐

  1. C算法编程题系列

    我的编程开始(C) C算法编程题(一)扑克牌发牌 C算法编程题(二)正螺旋 C算法编程题(三)画表格 C算法编程题(四)上三角 C算法编程题(五)“E”的变换 C算法编程题(六)串的处理 C算法编程题 ...

  2. PHPMailer不能发送邮件

    PHPMailer不能连接SMTP服务器,和修改SMTP大小写没有关系 (2011-10-22 12:17:35) 转载▼ 标签: php phpmailer 杂谈 分类: 默认分类 PHPmaile ...

  3. iOS注册,找回密码时用到的获取验证码

    #import "ViewController.h" #import "NSTimer+BlocksKit.h" @interface ViewControll ...

  4. 一个资深iOS开发者对于React Native的看法

    一个资深iOS开发者对于React Native的看法 当我第一次尝试ReactNative的时候,我觉得这只是网页开发者涉足原生移动应用领域的歪门邪道.   我认为一个js开发者可以使用javasc ...

  5. Android:理解Fragment

    最近都在公司搞测试,静不下心来学android.今天就把Fragment搞懂吧. Fragment的几点要点: 1.用于大屏幕平板,容纳更多组件,可复用2.Fragment必须嵌入Activity中 ...

  6. UIView之基础控件综合事例

    九宫格算法实现及优化 2015年11月19日 星期四 23:21 1.九宫格之初实现 要求: 1.1在主界面中,点击添加按钮就能添加商品,点击删除按钮就能删除商品; 1.2在初始化界面中,添加按钮处于 ...

  7. 将树莓派Raspberry Pi设置为无线路由器&lpar;WiFi热点AP&comma;RTL8188CUS芯片&rpar;

    http://wangye.org/blog/archives/845/ 最近又开始折腾起Raspberry Pi来了,因为某处上网需要锐捷拨号,于是我就想能不能让我的树莓派代劳,当然首先要将其改造为 ...

  8. vs自己主动生成的WebService配置文件在部署到IIs6后,服务调用失败的解决方法

    近日.在项目中须要引用java公布的WebService,加入服务引用后,调用一切正常. 配置例如以下: <system.serviceModel> <bindings> &l ...

  9. SqlAlchemy操作(二)

    SQLALchemy初始化链接数据库 1. 数据库配置. https://www.cnblogs.com/mengbin0546/p/10124560.html 2.  python端操作. 一.  ...

  10. MySQL 、SQL MS Access、和 SQL Server 数据类型

    MySQL 数据类型 在 MySQL 中,有三种主要的类型:Text(文本).Number(数字)和 Date/Time(日期/时间)类型. Text 类型: 数据类型 描述 CHAR(size) 保 ...