一直对Flex组件的线程安全性有所担心,所以一直想测试一下Flex的线程同步问题。 但由于Flex本身不提供任何的多线程处理的编程接口,所以没有办法通过程序直接新建多个线程进行测试。不过,Flex提供的事件处理机制一般都理解成为一个异步的处理,所以很有可能Flex会为每个事件的处理新建一个新的线程分别处理。首先,为了确定事件的处理实现是否是多线程的,我做了这个试验。
我本地的运行环境是:
OS:windows xp sp2
IDE: Eclipse + Adobe Flex Builder 2.0.1 plugin
jdk : jdk 1.5.0_11
browser: IE6
flashplayer: FlashPlayer 9
工程结构:
在工程的根目录下,新建以下文件:
MutiThreadTest.mxml
MyApplication.mxml
MyButton.mxml
MyEvent.as
这些文件的实现代码如下:
MutiThreadTest.mxml:
xmlns:mx = " http://www.adobe.com/2006/mxml "
xmlns:test = " * "
layout = " absolute "
newProcessor = " createProcessor(event) " >
< mx:Script >
<! [CDATA[
import flash.utils.setTimeout;
private function testStart() : void {
for (var i : uint = 0 ; i < 10 ; i ++ ) {
this .dispatchEvent( new MyEvent( " newProcessor " , i));
trace( " processor " + i + " has been dispatched " );
}
}
private function createProcessor( event : Event) : void {
var myEvent : MyEvent = event as MyEvent;
var index : uint = myEvent.index;
var date : Date = new Date();
trace( " ====================================== " );
trace( " Event index : " + index);
trace( " -------------------------------------- " );
trace( " Start time : " + date);
for (var i : uint = ( 10 - index) * 1000000000000 ; i > 0 ; i -- )
;
trace( " end time : " + ( new Date()) );
trace( " ====================================== " );
}
]] >
</ mx:Script >
< mx:VBox >
< mx:Button id = " startBtn " label = " Test Start " click = " testStart() " />
</ mx:VBox >
</ test:MyApplication >
MyApplication.mxml
xmlns:mx = " http://www.adobe.com/2006/mxml " >
< mx:Metadata >
[Event(name = " newProcessor " , type = " MyEvent " )]
</ mx:Metadata >
</ mx:Application >
MyButton.mxml
< mx:Metadata >
[Event( " newProcessor " )]
</ mx:Metadata >
< mx:Script >
<! [CDATA[
private function onClick( event : Event) : void {
this .dispatchEvent( new Event( " newProcessor " );
}
]] >
</ mx:Script >
</ mx:Button >
MyEvent.as
import flash.events.Event;
public class MyEvent extends Event {
private var _index : uint ;
public function MyEvent(eventName : String, index : uint ){
super(eventName);
this ._index = index;
}
public function get index() : uint {
return this ._index;
}
}
}
其中,MutiThreadTest.mxml为程序的运行入口。
当程序启动后,点击按钮,testStart()就会被执行。然后,这个方法会连续触发十个MyEvent类型的事件。接着系统就会为每个事件调用指定的事件处理方法createProcessor(event : Event)。这个方法的主要运行时间是花在
;
这一段空循环中。并且,对于越早触发的事件,这个循环处理的次数越少,所需的时钟周期也越少。同时,当程序运行在debug模式下时,在这个循环执行的前后,会在控制台打印出触发事件的index以及循环开始和结束的时间。
如果Flex的事件处理机制实现是多线程的,那么,由于触发事件所需的时钟周期应当远少于createProcessor(event : Event)处理所需时钟周期。因而,在所有的十个事件全部被触发后,应当还有运行事件处理方法的线程还在运行,也就是说应当了会出现线程并行运行的情况,其表现应当就是若干个事件处理方法在控制台上的输出是混合在一起的。那么,实际的运行结果是否会是这样呢?
运行结果:
======================================
Event index : 0
--------------------------------------
Start time : Tue Jan 22 13 : 18 : 50 GMT + 0800 2008
end time : Tue Jan 22 13 : 18 : 58 GMT + 0800 2008
======================================
processor 0 has been dispatched
======================================
Event index : 1
--------------------------------------
Start time : Tue Jan 22 13 : 18 : 58 GMT + 0800 2008
end time : Tue Jan 22 13 : 19 : 06 GMT + 0800 2008
======================================
processor 1 has been dispatched
======================================
Event index : 2
--------------------------------------
Start time : Tue Jan 22 13 : 19 : 06 GMT + 0800 2008
end time : Tue Jan 22 13 : 19 : 16 GMT + 0800 2008
======================================
processor 2 has been dispatched
======================================
Event index : 3
--------------------------------------
Start time : Tue Jan 22 13 : 19 : 16 GMT + 0800 2008
end time : Tue Jan 22 13 : 19 : 29 GMT + 0800 2008
======================================
processor 3 has been dispatched
======================================
Event index : 4
--------------------------------------
Start time : Tue Jan 22 13 : 19 : 29 GMT + 0800 2008
end time : Tue Jan 22 13 : 19 : 45 GMT + 0800 2008
======================================
processor 4 has been dispatched
======================================
Event index : 5
--------------------------------------
Start time : Tue Jan 22 13 : 19 : 45 GMT + 0800 2008
end time : Tue Jan 22 13 : 19 : 47 GMT + 0800 2008
======================================
processor 5 has been dispatched
======================================
Event index : 6
--------------------------------------
Start time : Tue Jan 22 13 : 19 : 47 GMT + 0800 2008
Error: Error # 1502 : A script has executed for longer than the default timeout period of 15 seconds.
at MutiThreadTest / MutiThreadTest::createProcessor()[C:developmentworkspaceFlexLearningMutiThreadTest.mxml: 35 ]
at MutiThreadTest / ___MyApplication1_newProcessor()[C:developmentworkspaceFlexLearningMutiThreadTest.mxml: 5 ]
at flash.events::EventDispatcher / flash.events:EventDispatcher::dispatchEventFunction()
at flash.events::EventDispatcher / dispatchEvent()
at mx.core::UIComponent / dispatchEvent()[C:dev lex_201_gmcsdk rameworksmxcoreUIComponent. as : 8323 ]
at MutiThreadTest / MutiThreadTest::testStart()[C:developmentworkspaceFlexLearningMutiThreadTest.mxml: 22 ]
at MutiThreadTest / __startBtn_click()[C:developmentworkspaceFlexLearningMutiThreadTest.mxml: 50 ]
processor 6 has been dispatched
======================================
Event index : 7
--------------------------------------
Start time : Tue Jan 22 13 : 20 : 00 GMT + 0800 2008
end time : Tue Jan 22 13 : 20 : 08 GMT + 0800 2008
======================================
processor 7 has been dispatched
======================================
Event index : 8
--------------------------------------
Start time : Tue Jan 22 13 : 20 : 08 GMT + 0800 2008
end time : Tue Jan 22 13 : 20 : 19 GMT + 0800 2008
======================================
processor 8 has been dispatched
======================================
Event index : 9
--------------------------------------
Start time : Tue Jan 22 13 : 20 : 19 GMT + 0800 2008
end time : Tue Jan 22 13 : 20 : 32 GMT + 0800 2008
======================================
processor 9 has been dispatched
程序运行结果完全出乎我的意料,这些事件的处理,甚至以及事件处理方法的调用,完全是同步的!!!!@0@
我还是有点不甘心,于是我又想尝试一下使用flash.utils包中的setTimeout方法。因为这个方法需要进行计时,一般来说计时的工具都是交给一个daemon进行的,虽然最终调用处理方法也是使用事件,不过说不定会有意外的效果。于是,我对MutiThreadTest.mxml做了一些修改:
xmlns:mx = " http://www.adobe.com/2006/mxml "
xmlns:test = " * "
layout = " absolute "
newProcessor = " createProcessor(event) " >
< mx:Script >
<! [CDATA[
import flash.utils.setTimeout;
private function testStart() : void ... {
for (var i : uint = 0; i < 10; i++) ...{
this.dispatchEvent(new MyEvent("newProcessor", i));
trace("processor " + i + " has been dispatched");
}
}
private function createProcessor( event : Event) : void ... {
var myEvent : MyEvent = event as MyEvent;
var index : uint = myEvent.index;
var date : Date = new Date();
trace("======================================");
trace("Event index : " + index);
trace("--------------------------------------");
trace("Before setting a timer || " + index);
setTimeout(timeoutHandler, 100, index);
trace("After setting a timer || " + index)
trace("Start time : " + date + " || " + index );
for (var i : uint = (10 - index) * 1000000000000; i > 0; i--)
;
trace("end time : " + (new Date()) + " || " + index);
trace("======================================");
}
private function timeoutHandler(index : uint) : void ... {
trace("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");
trace("timer handler created by event " + index);
trace("handling time : " + (new Date()));
trace(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
}
]] >
</ mx:Script >
< mx:VBox >
< mx:Button id = " startBtn " label = " Test Start " click = " testStart() " />
</ mx:VBox >
</ test:MyApplication >
这里新加了一个setTimeout()方法的调用,并且触发时间设得比较短,根据之前程序情况来判断, createProcessor()执行完之前,如果计时触发是并行处理的话,timeoutHandler() 就会被调用,这样这两个方法的控制台输出就应当是混合在一起的。那么实际的运行结果是怎么样的呢?
运行结果如下:
======================================
Event index : 0
--------------------------------------
Before setting a timer || 0
After setting a timer || 0
Start time : Wed Jan 23 14:21:55 GMT+0800 2008 || 0
end time : Wed Jan 23 14:22:00 GMT+0800 2008 || 0
======================================
processor 0 has been dispatched
======================================
Event index : 1
--------------------------------------
Before setting a timer || 1
After setting a timer || 1
Start time : Wed Jan 23 14:22:00 GMT+0800 2008 || 1
end time : Wed Jan 23 14:22:08 GMT+0800 2008 || 1
======================================
processor 1 has been dispatched
======================================
Event index : 2
--------------------------------------
Before setting a timer || 2
After setting a timer || 2
Start time : Wed Jan 23 14:22:08 GMT+0800 2008 || 2
end time : Wed Jan 23 14:22:19 GMT+0800 2008 || 2
======================================
processor 2 has been dispatched
======================================
Event index : 3
--------------------------------------
Before setting a timer || 3
After setting a timer || 3
Start time : Wed Jan 23 14:22:19 GMT+0800 2008 || 3
end time : Wed Jan 23 14:22:32 GMT+0800 2008 || 3
======================================
processor 3 has been dispatched
======================================
Event index : 4
--------------------------------------
Before setting a timer || 4
After setting a timer || 4
Start time : Wed Jan 23 14:22:32 GMT+0800 2008 || 4
end time : Wed Jan 23 14:22:48 GMT+0800 2008 || 4
======================================
processor 4 has been dispatched
======================================
Event index : 5
--------------------------------------
Before setting a timer || 5
After setting a timer || 5
Start time : Wed Jan 23 14:22:48 GMT+0800 2008 || 5
end time : Wed Jan 23 14:22:50 GMT+0800 2008 || 5
======================================
processor 5 has been dispatched
======================================
Event index : 6
--------------------------------------
Before setting a timer || 6
After setting a timer || 6
Start time : Wed Jan 23 14:22:50 GMT+0800 2008 || 6
Error: Error #1502: A script has executed for longer than the default timeout period of 15 seconds.
at MutiThreadTest/MutiThreadTest::createProcessor()[C:/development/workspace/FlexLearning/MutiThreadTest.mxml:41]
at MutiThreadTest/___MyApplication1_newProcessor()[C:/development/workspace/FlexLearning/MutiThreadTest.mxml:5]
at flash.events::EventDispatcher/flash.events:EventDispatcher::dispatchEventFunction()
at flash.events::EventDispatcher/dispatchEvent()
at mx.core::UIComponent/dispatchEvent()[C:/dev/flex_201_gmc/sdk/frameworks/mx/core/UIComponent.as:8323]
at MutiThreadTest/MutiThreadTest::testStart()[C:/development/workspace/FlexLearning/MutiThreadTest.mxml:22]
at MutiThreadTest/__startBtn_click()[C:/development/workspace/FlexLearning/MutiThreadTest.mxml:277]
processor 6 has been dispatched
======================================
Event index : 7
--------------------------------------
Before setting a timer || 7
After setting a timer || 7
Start time : Wed Jan 23 14:23:01 GMT+0800 2008 || 7
end time : Wed Jan 23 14:23:09 GMT+0800 2008 || 7
======================================
processor 7 has been dispatched
======================================
Event index : 8
--------------------------------------
Before setting a timer || 8
After setting a timer || 8
Start time : Wed Jan 23 14:23:09 GMT+0800 2008 || 8
end time : Wed Jan 23 14:23:20 GMT+0800 2008 || 8
======================================
processor 8 has been dispatched
======================================
Event index : 9
--------------------------------------
Before setting a timer || 9
After setting a timer || 9
Start time : Wed Jan 23 14:23:20 GMT+0800 2008 || 9
end time : Wed Jan 23 14:23:33 GMT+0800 2008 || 9
======================================
processor 9 has been dispatched
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
timer handler created by event 0
handling time : Wed Jan 23 14:23:33 GMT+0800 2008
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
timer handler created by event 1
handling time : Wed Jan 23 14:23:33 GMT+0800 2008
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
timer handler created by event 2
handling time : Wed Jan 23 14:23:33 GMT+0800 2008
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
timer handler created by event 3
handling time : Wed Jan 23 14:23:33 GMT+0800 2008
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
timer handler created by event 4
handling time : Wed Jan 23 14:23:33 GMT+0800 2008
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
timer handler created by event 5
handling time : Wed Jan 23 14:23:33 GMT+0800 2008
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
timer handler created by event 6
handling time : Wed Jan 23 14:23:33 GMT+0800 2008
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
timer handler created by event 7
handling time : Wed Jan 23 14:23:33 GMT+0800 2008
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
timer handler created by event 8
handling time : Wed Jan 23 14:23:33 GMT+0800 2008
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
timer handler created by event 9
handling time : Wed Jan 23 14:23:33 GMT+0800 2008
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
运行结果再次让我大跌眼镜……
目前我还没想出一个比较简单明确的解释。
我也有查看过source code,不过目前只有Application的超类UIComponent是开源的。它的dispatch方法只做了两件事,调用了一个mx_internal的方法dispatchEventHook,然后又调用了它的超类的dispatch方法。这个方法的实现目前不是开源的,处理机制也是不得而知。
不过,Adobe的相关文档说他们不保证先触发的事件会被先处理(my boss told me^^),所以,实际上具体的实现机制是跟Flex的API实现以及FlashPlayer的VM实现相关的,以后新的版本中可能会有变化。也许,有可能,这种情况会持续下去,因为毕竟线程安全是个复杂的问题,而且看来Flex也没打算提供可编程的同步机制。在这种情况下,只使用同步处理的机制,或者说尽可能少的使用多线程处理,是一种比较安全稳妥的解决办法。
话又说回来,至少在目前的运行环境下,不要让Flex运行过于耗时的复杂运算,因为很可能会让整个系统失去响应,毕竟你不能启动一个新的线程来在后台处理这些逻辑,同时让UI保持对用户操作的响应。