多线程下WinForm开发应该注意哪些问题?

转载 2012年03月27日 15:45:48
1. 标准WinForm控件不支持多线程访问

这一点,其实是Windows的机制。.NET 中每一个Control其实都是一个Window,使用这些Window,原则上都应该在创建这个Window的线程中。否则,会产生异常。这一点,似乎Windows也没有强制约束。某些操作可能会扔异常,而有些情况下却不会。比如:访问这个Window的某些属性。

因此,.NET在Control上暴露了Invork方法,以实现将操作发送到Control所属的线程中执行。细节,可以参考我以前的一篇帖子。

这一点,已经是标准做法。所以不能称之为Bug。

现在我们来看一下出现了什么Bug。用户的应用程序中,某些时候需要启动一个新的线程,在这个线程中构造并显示一个Form,其中包含我们的控件。当用户启动后,发现我们的产品不能够正确地显示。

什么原因呢?


2. 静态成员是元凶

根据经验,这种情况下的问题一般都出在静态成员上。我们在开发中,经常为了优化性能,而将一些对象缓存在静态成员中。如果我们将一个包含Window的对象缓存在静态对象中,对它的调用就可能会产生异常。

大家都知道,静态成员是属于整个AppDomain的,也就是说,所有的线程在共享同一个静态对象。当另外一个线程调用静态对象上的方法时,根据前面的规则(WinForm控件不支持多线程访问),异常产生了。

呵呵,这一点,是我在以前修Bug中的经验。但是,昨天似乎并不灵验。整个出现异常的部分没有发现缓存静态的Window对象。

经验告诉我,肯定是静态成员惹得祸。先看出问题的代码,发现这里缓存了一个静态的Bitmap。

难道是,这个Bitmap不允许跨线程访问?对这个Bitmap对象进行了锁(Lock)操作后,发现问题解决。于是得出了下面的经验。


3. 某些GDI对象也不允许跨线程访问

GDI对象(包括GDI+对象)都是有Handle的。可能在某些情况下,微软也不保证跨线程访问的可靠性。从昨天的调试结果来看,这些对象应该是不允许多个线程并发访问。这一点并没有太多的跟踪和调试,如果你有兴趣,可以尝试的再跟一下。

通过对这些对象加锁,避免并发访问,似乎问题已经解决。但是因为这一部分被使用的非常频繁(否则我们也不加Cache了),加锁后,发现对控件的性能产生很大的影响。看来仅仅加锁是不能解决所有问题的。

于是,昨天从Winking那里学了一着。


4. System.ThreadStaticAttribute

这个属性标示一个静态对象在每个线程中是独立的。因此,我们只要在这些缓存字段上加上这个属性。哈哈,问题解决。喔,看起来解决的太轻松了。先别急,现实往往比想象中残酷。


5. 静态对象的初始化的问题

我们一般会使用两种方式初始化静态对象。静态构造函数 和 第一次访问时。(我们经常会在静态对象的声明后面直接进行初始化,这种情况其实也是通过静态构造函数进行初始化的。只是编译器帮助我们将这些代码移动到了静态构造函数里面。因此,这里不单独讨论。)

如果我们将静态字段标记为线程唯一的,静态构造函数就不能够正确地初始化这个字段了。因为,静态构造函数只被调用一次。(它没有办法标记为线程唯一的:))

于是,这里就需要使用延迟构建模式。我们需要将这些静态成员声明为属性,在其Get函数中判断对象是否为空(或默认值,值类型)。从而确定是否需要构造缓存对象。 


6. 总结

呵呵,至此,所有的问题都解决了,Bug也已经修复。总体来说,静态对象始终是一个不安全因素。在目前我负责的项目中,我们是尽量避免使用静态成员。如果需要缓存对象,我们也会将这些缓存对象放在一个特定的池中。确保缓存对象是面向特定实例的而不是全局的。避免出现上面的问题。

相关文章推荐

C#Winform中多线程访问控件问题,可帮助解决“线程间操作无效: 从不是创建控件“FrmUpdate”的线程访问它。”异常

我们在做winform应用的时候,大部分情况下都会碰到使用多线程控制界面上控件信息的问题。然而我们并不能用传统方法来做这个问题,下面我将详细的介绍。    首先来看传统方法:        ...

解决多线程下WinForm卡死问题

在WinForm窗体中,会根据需要为窗体放置一个Timer组件来实现定时执行某个任务的功能。例如下面的程序: 版本1 public partial class MainForm : Form { ...
  • wienee
  • wienee
  • 2013-03-19 16:32
  • 1545

c#中使用多线程访问winform中控件的问题

c#中使用多线程访问winform中控件的问题 我们在做winform应用的时候,大部分情况下都会碰到使用多线程控制界面上控件信息的问题。然而我们并不能用传统方法来做这个问题,下面我将详细的介绍...

C# WinForm多线程开发(二) ThreadPool 与 Timer

原文地址:点击打开链接 [摘要]本文介绍C# WinForm多线程开发之ThreadPool 与 Timer,并提供详细的示例代码供参考。

C# WinForm多线程开发(一) Thread类库

原文地址:点击打开链接

C#区域性语言(CultureInfo)与资源本地化(WINFORM,多线程,原理可适用于网络开发)

有时候我们会按照特定需要来修改系统的显示语言(资源),本文即简要地描述了修改当前线程的区域性并使用本地化资源展现。 首先对需要使用不同区域性设置的用户界面设置不同的语言资源: 选择窗体,在属性栏中...
  • Joke_C
  • Joke_C
  • 2012-03-30 01:10
  • 3256

C# WinForm多线程开发(三) Control.Invoke

原文地址:点击打开链接

C# Winform 多线程下载

  • 2017-09-14 16:15
  • 3.20MB
  • 下载

IT form整理 java应该注意的问题3(异常+多线程)

12,运行时异常与一般异常有何异同 (1)运行时异常属于程序缺陷造成的,是设计或实现上的问题,即,如果程序设计良好,这类异常便不会发生;该类异常在语法上不强制程序员必须处理,成为非受检异常。  常见的...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)