About threads
In any desktop programming language like C#, Java or even VB, a developer has the ability of performing certain tasks in seperate threads, building a multi-threading application.
A thread is a part of a process that functions independently and seperatly and can only interact through system-provided inter-process communication mechanisms.
This increases the performance of the application and saves the CPU from any high-duty work.
In Actionscript however, there is no custom threading, no threads at all, meaning Actionscript is single-threaded.
That also means unfortunatly, when doing some heavy computation, the UI of your application cannot be updated while you’re doing that heavy computation, so your application appears stuck or effects don’t run smoothly.
When I was developing the FLVRecorder classes, I encountered this problem, cursing Satan. Converting the saved bitmaps was so intense that the application froze.
A Thread class like in Java, could come in real handy on moments like that, but there is none.
When I was doing some r&d on how to split up code, simulating threads, I found a PseudoThread class by Alex Harui that could simulate a thread in the application on the Adobe Blogs (link: here).
This pseudo-threading works well, it does enhance perfomance a bit, and the most important, the application only slowed down & did not freeze when saving.
But there is a problem with this class, it only works with Flex, because it is using a Flex specific class, called SystemManager (from the mx.managers package).
Using threads in Flash
We are going to convert this PseudoThread class so we can use it in a Flash application and to use this class in Flash, we need to replace the SystemManager class with something else without losing the performance enhancements.
After trying some combinations, I came up with this very good solution.
Here is how I did it…
The class that I found on the Adobe Blogs, uses a UIComponent added to the systemManager to run the code on in a enterframe. I replaced this UIComponent by a Sprite, and the systemManager by the stage.
First, I changed the constructor of the PT class by replacing the systemManager variable with an instance of the stage.
Then I added a global variable sm, from type Sprite and initialized this variable in the constructor and added it to the stage instance that is passed on in the constructors parameters.
I did some changing on the event listeners, by setting the weakreference parameter on true (adding ‘,false, 0, true’ after defining eventhandling function), allowing the FP to destroy the event listeners easier.
How it looks like
package be.boulevart.threading {
import flash.display.Sprite;
import flash.display.Stage;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.utils.getTimer;
public class PseudoThread extends EventDispatcher {
// number of milliseconds we think it takes to render the screen
public var RENDER_DEDUCTION : int = 20;
private var fn : Function;
private var obj : Object;
private var thread : Sprite;
private var start : Number;
private var due : Number;
private var sm : Sprite
private var mouseEvent : Boolean;
private var keyEvent : Boolean;
public function PseudoThread(yourStage : Stage,threadFunction : Function,threadObject : Object = null) {
fn = threadFunction;
sm = new Sprite()
yourStage.addChild(sm)
if(obj == null) {
obj = threadObject;
}else {
obj = new Object()
}
// add high priority listener for ENTER_FRAME
sm.stage.addEventListener(Event.ENTER_FRAME , enterFrameHandler , false , 200 , true);
sm.stage.addEventListener(MouseEvent.MOUSE_MOVE , mouseMoveHandler , false , 0 , true);
sm.stage.addEventListener(KeyboardEvent.KEY_DOWN , keyDownHandler , false , 0 , true);
thread = new Sprite();
sm.addChild(thread);
thread.addEventListener(Event.RENDER , renderHandler);
}
private function enterFrameHandler(event : Event) : void {
start = getTimer();
var fr : Number = Math.floor(1000 / thread.stage.frameRate);
due = start + fr;
thread.stage.invalidate();
thread.graphics.clear();
thread.graphics.moveTo(0 , 0);
thread.graphics.lineTo(0 , 0);
}
private function renderHandler(event : Event) : void {
if (mouseEvent || keyEvent)
due -= RENDER_DEDUCTION;
while (getTimer() < due) {
if(obj == null) {
if (!fn()) {
if (!thread.parent)
return;
sm.stage.removeEventListener(Event.ENTER_FRAME , enterFrameHandler);
sm.stage.removeEventListener(MouseEvent.MOUSE_MOVE , mouseMoveHandler);
sm.stage.removeEventListener(KeyboardEvent.KEY_DOWN , keyDownHandler);
sm.removeChild(thread);
thread.removeEventListener(Event.RENDER , renderHandler);
thread = null
thread = new Sprite()
dispatchEvent(new Event("threadComplete"));
}
}else {
if (!fn(obj)) {
if (!thread.parent)
return;
sm.stage.removeEventListener(Event.ENTER_FRAME , enterFrameHandler);
sm.stage.removeEventListener(MouseEvent.MOUSE_MOVE , mouseMoveHandler);
sm.stage.removeEventListener(KeyboardEvent.KEY_DOWN , keyDownHandler);
sm.removeChild(thread);
thread = null
thread = new Sprite()
thread.removeEventListener(Event.RENDER , renderHandler);
dispatchEvent(new Event("threadComplete"));
}
}
}
mouseEvent = false;
keyEvent = false;
}
private function mouseMoveHandler(event : Event) : void {
mouseEvent = true;
}
private function keyDownHandler(event : Event) : void {
keyEvent = true;
}
}
}
This class works in Flash (FP9) & Flex.
Conclusion
Pseudo threading is a bit painfull, it’s not the real deal and it’s slow, but it’s the best we have right now.
This can make the difference between a slow app or a smoothly running one.
One day, if Adobe is feeling like it, threading will be build in, since more and more RIA builders long for it and since the competition is getting tougher (Sliverlight has build in threading for example).
Download
Download the PseudoThread class.
Recent comments