昨日,与一同事一起在修一个多线程下使用我们的控件产品的Bug。现将相关的经验发布在这里。
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也已经修复。总体来说,静态对象始终是一个不安全因素。在目前我负责的项目中,我们是尽量避免使用静态成员。如果需要缓存对象,我们也会将这些缓存对象放在一个特定的池中。确保缓存对象是面向特定实例的而不是全局的。避免出现上面的问题。
相关文章:.NET下跨线程访问Control。
http://www.cnblogs.com/Cajon/archive/2006/06/23/433642.html
转自 cajon
分享到:
相关推荐
在C# Winform应用中实现多线程下载是一项常见的任务,尤其在处理大文件或需要提高下载速度的情况下。本文将详细讲解如何利用C#的多线程技术来创建一个Winform应用程序,实现高效的文件下载功能。 首先,我们需要...
综上所述,本文档深入探讨了在多线程环境下,如何通过ISynchronizeInvoke接口和Winform的委托来安全地进行UI操作,这是在C#中开发Winform应用程序时经常会遇到且需要妥善解决的问题。通过理解和运用这些知识点,...
在IT领域,多线程定时器是一个非常实用的工具,特别是在开发Web应用程序和Windows桌面应用(Winform)时。这个工具允许程序员在多个线程环境中设置定时任务,从而实现后台处理、定期检查更新或者执行一系列间隔操作...
在Windows CE(WinCe)平台上开发应用程序时,经常会遇到多线程环境下的UI交互问题。标题和描述提及的“WinCe多线程访问winform中控件”是一个关键的编程挑战,因为不正确的操作可能会导致异常,如线程竞态条件、...
在多线程环境中,需要注意线程同步问题,因为WebBrowser控件的大部分操作是线程不安全的,必须在UI线程中进行。当我们从后台线程调用`InvokeScript`后,需要确保结果的处理或者对WebBrowser控件的其他修改回到UI线程...
在Windows Forms(WinForm)应用开发中,多线程技术是一项关键技能,它允许应用程序同时执行多个任务,提高程序响应速度并优化资源管理。本文将深入探讨如何在WinForm应用中自定义多线程,以实现高效且用户友好的...
在C#编程中,实现Http多线程下载是一项常见的任务,尤其在处理大文件或需要提高下载速度的情况下。本文将详细讲解如何利用C# Winform框架实现这一功能,并结合多线程技术提升下载效率。 首先,我们需要理解Http协议...
总之,C# WinForm中利用多线程技术可以有效地解决界面快速刷新问题。通过合理地使用`Task`、`async/await`以及线程同步方法,我们可以创建出流畅、响应迅速的用户界面。同时,开发者还应关注线程间的通信和数据同步...
为了解决这个问题,开发者通常会采用多线程技术,将耗时任务放在后台线程执行,同时确保UI线程能及时更新进度和状态信息。本文将详细讲解如何在WinForm应用中实现多线程异步更新UI。 1. **线程基础知识**:在...
在C#编程环境中,开发Windows桌面应用程序(WinForms)或嵌入式系统应用程序(Windows CE,简称WINCE)时,多线程技术是一项至关重要的技能。多线程允许程序同时执行多个任务,提高应用程序的响应性和效率。下面将...
在C# Winform开发中,多线程技术是不可或缺的一部分,尤其当涉及到用户界面(UI)的异步操作时。线程允许程序同时执行多个任务,提高应用的响应性和效率。本话题将深入探讨如何利用线程来延迟创建子窗体,并且如何安全...
在Windows Forms(WinForm)应用程序中,`DataGridView`控件是一种常用的数据展示工具,它可以方便地显示和编辑表格数据。...在编写代码时,时刻注意线程安全,是开发高效、可靠的多线程WinForm应用的关键。
C# WinForm 多线程开发复习进程 ...C# WinForm 多线程开发是一种复杂的编程技术,需要了解多线程机制、线程间的同步和通信、避免死锁和资源竞争等问题。但是,如果正确使用多线程技术,可以提高程序的效率和性能。
在Windows Forms(winForm)应用开发中,多线程操作UI是提高应用程序性能和响应性的重要技术。在传统的单线程环境中,UI更新和后台任务往往在同一线程中执行,导致用户界面在处理耗时任务时可能出现卡顿或无响应。...
C# WinForm多线程开发是软件开发中一个关键的领域,尤其是在处理耗时操作或者需要并发执行任务的应用程序中。本文将深入探讨C#中的多线程概念,以及如何在WinForm应用程序中安全地使用线程。 首先,理解线程和进程...
SUM分页控件(支持WinForm多线程)源码 源码描述: 一、源码特点 本软件是一个WinForm分页控件演示程序。其中分页控件简洁高效,支持多线程的跨线程安全访问,是开发数据展示软件的优秀分页控件。 二、功能介绍 1...
5. **异常处理**: 虽然委托提供了安全访问UI控件的方式,但在多线程环境下,仍需注意同步问题和可能的线程冲突。合理使用`lock`关键字或其他同步机制,避免资源竞争。 6. **控制台应用程序中的WinForm控件访问**: ...
在多线程场景下,每个部分可以由不同的线程处理,但同样需要注意线程同步以防止UI更新问题。 总之,这个.NET 2.0的多线程实例涵盖了多线程的创建与管理、线程同步、UI更新、文件系统操作、异步编程等多个核心知识点...
在Windows Forms(Winform)应用程序开发中,多线程技术是一项关键技能,特别是在处理耗时操作时,如大数据处理、网络通信或者长时间计算等。这些操作如果在主线程上执行,将导致用户界面(UI)无响应,影响用户体验...
在C# WinForm开发中,我们常常遇到这样一个问题:当执行耗时操作(如大量数据处理、网络请求等)时,界面会变得无响应,即“卡住”。为了解决这个问题,我们需要理解并运用线程和委托的概念。本文将详细阐述如何在C#...