原文地址 http://twaver.servasoft.com/?p=658
继第一和第二篇后,GUI线程安全的原理性内容基本就这些了,如果你是搞学术理论研究的基本就不用继续阅读下面几篇我要继续八卦的内容,下面的内容都是针对具体技术平台的细节问题了。
static void Main(string[] args)
{
TextBlock text = new TextBlock();
}
创建个Console程序,敲出上面的和helloworld一样复杂度的代码,运行后你估计会怀疑自己的智商,这么简单的代码都能搞出异常?
还真是再简单问题都有其复杂的一面,我总在想为什么傻人有傻福,因为傻人傻傻的认真对待每个简单的事情所以搞透了其复杂的本质,所谓的聪明人都去研究哥德巴赫猜想,日积月累傻人掌握了众多事物本质成了有福之人,所谓聪明者竟然没搞明白一加一为什么等于二。
private InputManager()
{
// STA Requirement
//
// Avalon doesn't necessarily require STA, but many components do. Examples
// include Cicero, OLE, COM, etc. So we throw an exception here if the
// thread is not STA.
if(Thread.CurrentThread.GetApartmentState() != ApartmentState.STA)
{
throw new InvalidOperationException(SR.Get(SRID.RequiresSTA));
}
看看System.Windows.Input.InputManager的代码实现你就明白了,原来TextBlock的创建要求在STA环境中,你要问我啥叫STA,我喜欢将其简单理解为这玩意儿就是我们前面提到的EDT,也就是那个全局唯一的UI Thread,具体的解释你可以看Component_Object_Model#Threading_in_COM的解释,当年读潘爱民翻译的《COM本质论》差点让我没了继续当程序员的勇气,以下引用了一段话你就看着玩就行
There are three types of Apartment Models in the COM world: Single-Threaded Apartment (STA), Multi-Threaded Apartment (MTA), and Neutral Apartment. Each apartment represents one mechanism whereby an object’s internal state may be synchronized across multiple threads.
The Single-Threaded Apartment (STA) model is a very commonly used model. Here, a COM object stands in a position similar to a desktop application’s user interface. In an STA model, a single thread is dedicated to drive an object’s methods, i.e. a single thread is always used to execute the methods of the object. In such an arrangement, method calls from threads outside of the apartment are marshalled and automatically queued by the system (via a standard Windows message queue). Thus, there is no worry about race conditions or lack of synchronicity because each method call of an object is always executed to completion before another is invoked.
上面的一行代码其实已经说明了问题Thread.CurrentThread.GetApartmentState() != ApartmentState.STA,也就是main启动运行的线程并不是EDT,回想一下WindowsForms和WPF的应用程序的main是怎么写的,是的就是这个[STAThread]起的作用,把上面代码加上这个[STAThread]你就不会再怀疑自己智商了。
[STAThread]
static void Main()
{
... ...
}
《Applications = Code + Markup》这本是我学习WPF的第一本书,讲解思路非常符合传统程序员口味,上半本先完全用OO的方式解释了WPF组件的来龙去脉,下半本才开始设计XAML的标签应用,说实话我很看不惯现在的快餐式书籍,一上来就是XAML用户连WPF组件Dependency Properties这种最重要的新机制都完全不明白的情况下就开始写项目,同样的还有一上来就开始鼓吹如何用MXML快速创建Flex程序,本末倒置不介绍背后ActionScript语言实现原理的书籍,更有一大堆历史杯具人物写了多年JSP不知道啥是Java的OO。
前几天逛书店发现蔡学庸也翻译了《Applications = Code + Markup》一书,刚毕业时买过他的一本经典《Java夜未眠》,那时候作为个junior程序员的我手头也几个钱,晚上也没法去新天地泡mm的方式夜未眠,只好翻翻学庸兄的《Java夜未眠》,不过这些年薪水提高了反而不去书店买书了,原因很简单译书上得不到最新的技术资料而且翻译往往后失去作者的原味,相信《红楼梦》的译书很难接近原著的味道,扯远了,我不是要推销《Applications = Code + Markup》,更不是要打击蔡学庸译书的销量(我还是很尊重且喜欢订阅学庸兄的blog,如果学庸看到小弟此文希望能有机会交个朋友),只是想引用书中一段话帮助大家理解:
In any WPF program, the [STAThread] attribute must precede Main or the C# compiler will complain. This attribute directs the threading model of the initial application thread to be a single-threaded apartment, which is required for interoperability with the Component Object Model (COM). “Single-threaded apartment” is an old COM-era, pre-.NET programming term, but for our purposes you could imagine it to mean our application won’t be using multiple threads originating from the runtime environment.
使用TWaver Java的客户应该有印象,TWaver Java Demo的启动代码和地球上99%的Swing的main代码不一样,套了个SwingUtilities.invokeLater,曾经有个客户程序启动再大部分机器上都正常,结果在一台双核(N年前双核可是很洋相配置)的机器启动总出错,去现场帮忙找原因时我查的第一行代码就是main的启动代码,发现用户直接就在main里面创建各种UI组件,同时还起了Thread做各种业务,我就让他改成和TWaver Demo一样,启动时套了个SwingUtilities.invokeLater,你猜怎么样我竟然“被”允许回家了,这是我在赛瓦这些年最短的现场支持经历,像我这种不敢坐飞机的(去年做了38个小时火车去了哈尔滨培训客户)做了十几个小时的火车竟然现场待了几分钟就回家了,实在是亏啊。
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(){
public void run() {
Demo.init();
DemoFrame demoFrame = new DemoFrame();
demoFrame.setVisible(true);
}
});
}
很简单,因为main启动的函数并不是Swing的事件派发线程,SwingUtilities.isEventDispatchThread()你可以调用该函数测试一下,而地球99%的代码直接非EDT里面做UI的工作时极其危险的,大部分程序都是初始化界面然后show就结束了,这样基本能侥幸不出错,但如果你调用了show之后再有代码在执行,而且是操作到UI组件的地方拿基本必然会导致问题,但为了您的生命安全还请遵守GUI线程安全规则。这里有更多的解释可以参考:
public class MyApplication {
public static void main(String[] args) {
JFrame f = new JFrame(“Labels”);
// Add components to
// the frame here…
f.pack();
f.show();
// Don’t do any more GUI work here…
}
}
All the code shown above runs on the “main” thread. The f.pack() call realizes the components under the JFrame. This means that, technically, the f.show() call is unsafe and should be executed in the event-dispatching thread. However, as long as the program doesn’t already have a visible GUI, it’s exceedingly unlikely that the JFrame or its contents will receive a paint() call before f.show() returns. Because there’s no GUI code after the f.show() call, all GUI work moves from the main thread to the event-dispatching thread, and the preceding code is, in practice, thread-safe.
另外我挺喜欢现在.NET的线程安全问题上再底层框架做的检查工作,就像刚才Console操作TextBlock的代码,很容易程序员就能知道问题所在,而Swing这点上是最让我不满意的地方,你不提示检查我这些在普通线程操作UI组件就算了,某些情况下Swing还自作聪明的在内部帮你SwingUtilities.invoke***,结果常常导致很多客户质问我为什么我在普通线程调用revalidate()不出错,你们还非得让我以这么丑陋的代码方式到处去SwingUtilities.invoke,我觉得Swing这样的实现会误导程序员放松对线程安全的戒备,Swing仅仅在很少很少很少的函数上做了判断内部处理,如果大家觉得JComponent.revalidate()可以随意调用,那岂不TableModel、TreeModel…包括TWaver的DataBox和Element都可以不管EDT随意调用了,所以这点上.NET严谨的判断还是能让程序员尽早的规避了很多线程安全问题
public void revalidate() {
if (getParent() == null) {
// Note: We don't bother invalidating here as once added
// to a valid parent invalidate will be invoked (addImpl
// invokes addNotify which will invoke invalidate on the
// new Component). Also, if we do add a check to isValid
// here it can potentially be called before the constructor
// which was causing some people grief.
return;
}
if (SwingUtilities.isEventDispatchThread()) {
invalidate();
RepaintManager.currentManager(this).addInvalidComponent(this);
}
else {
Runnable callRevalidate = new Runnable() {
public void run() {
revalidate();
}
};
SwingUtilities.invokeLater(callRevalidate);
}
}
今天要讲的就这些内容,不知道昨天提到TWaver的FileTreeDemo里面还有个多线程的细节,你有没有注意到tree在加载文件是还能看到gif的loading图标,你只要打开tree.setEnableAnimation(true)开关,随时随意间任何element.setIcon(“/demo/tree/file/loading.gif”)设置上任何gif图标,twaver将透明自动的识别gif动画图标,并且让tree上的icon动画起来。这还没啥,有一回我看到用户启动画面竟然有很酷动画的splash screen,我问用户是不是嵌入了Flash,用户说:“用的就是你们的Network做启动界面,只不过放了一个GIF的大Node做主启动界面而已,赛瓦真该开除了你这个傻B,还号称TWaver Evangelist,丢人也不能丢这么大啊”
分享到:
相关推荐
《深入浅出Java》这本书以其独特的讲解方式,旨在让学习者轻松掌握复杂的Java编程语言。"深入浅出"这一理念,意味着作者通过直观、生动的示例和丰富的图解,帮助读者逐步理解Java的核心概念和技术。 Java是一种广泛...
### 深入浅出Java多线程程序设计 在当今高性能计算环境下,多线程技术已成为提升软件性能的关键手段之一。《深入浅出Java多线程程序设计》旨在为读者提供一个系统全面地理解Java多线程机制的平台。通过本篇文章,...
#### 三、《深入浅出MFC》书籍特点 1. **深入浅出**:本书的一大特点是将复杂的概念和难懂的技术点通过通俗易懂的语言进行讲解,让初学者也能轻松理解MFC的核心概念。 2. **实战案例**:书中包含了大量的实战代码...
4. **深入浅出MFC简体中文版**:Microsoft Foundation Classes (MFC) 是一个C++库,用于构建Windows应用程序。本书以易于理解的方式介绍了MFC的架构和用法,包括文档/视图架构、对话框、控件、数据库访问等,帮助...
它提供了标准库,包括了各种功能,比如图形用户界面GUI、网络编程、数据库连接等。这使得程序员在开发各种应用程序时可以方便地调用这些类库,提高开发效率。 另外,java还支持泛型编程。泛型可以让你编写更通用的...
《深入浅出MFC程序设计》是一本专为学习Microsoft Foundation Classes (MFC) 设计的书籍,旨在帮助读者深入理解和熟练运用MFC进行Windows应用程序开发。MFC是微软提供的一套C++类库,它封装了Windows API,使得...
《深入浅出Java语言程序设计》是一本专为Java初学者和进阶者精心编写的教程,旨在帮助读者全面理解并掌握Java编程的核心概念和技术。本书涵盖了从基础语法到高级特性的广泛内容,旨在使读者能够熟练运用Java进行软件...
《深入浅出MFC》是著名C++专家侯捷撰写的一本关于Microsoft Foundation Classes (MFC) 的经典著作。MFC是由微软开发的一个C++类库,它为Windows应用程序开发提供了一种抽象和封装,使得开发者可以更方便地利用...
《深入浅出:使用Python编程》是一本专为Python初学者设计的教程,旨在帮助读者快速掌握这门强大且易学的编程语言。Python作为现代软件开发中的主流语言,广泛应用于数据分析、机器学习、Web开发等多个领域。这本书...
本资源“深入浅出JAVASwing程序设计”旨在帮助开发者掌握Swing的基本概念、组件用法以及高级特性,从而能创建功能强大、交互性强的Java应用。 Swing在Java AWT(Abstract Window Toolkit)的基础上构建,提供了更轻...
本资源"深入浅出JAVA Swing程序设计 书+代码"旨在帮助开发者深入理解Swing,并通过实例代码进行实践,提升Java桌面应用的开发能力。 Swing提供了一套完全由Java实现的组件,这些组件不仅功能强大,而且在跨平台兼容...
本资源"深入浅出Java Swing程序设计_11394260.rar"显然是一个关于Java Swing编程的详细教程,涵盖了Swing的基本概念、组件使用、事件处理以及高级特性等。 Swing提供了丰富的组件集,包括按钮、文本框、标签、菜单...
《MFC深入浅出_从MFC设计到MFC编程》是一本专为对Microsoft Foundation Classes(MFC)感兴趣的开发者编写的指南。MFC是由微软公司开发的一个C++类库,它封装了Windows API,使得程序员可以使用面向对象的方式来编写...
《MFC深入浅出第二版源代码》是一个与知名编程图书配套的源代码资源,它为读者提供了实际操作和理解MFC(Microsoft Foundation Classes)框架的机会。MFC是微软为Windows应用程序开发提供的一种C++库,它封装了...