Tuesday, 23 February 2010

Micro Threads in ActionScript3 with open source Thrash

One of the major drawbacks of Flash Player is its lack of multithreading. In today’s market, when every new laptop comes with at least two processor cores and the cores aren’t getting faster this is a serious caveat. However, there is one other situation which also would benefit from threads. If you are doing any heavy data processing in your Flash/Flex app you know this; namely that the Flex GUI blocks while executing ActionScript.

While trying to make eXplorer perform better by using callLater() to delay some execution to the next frame and let the GUI update, I realized that I could abstract a pattern from this. But little did I know that pseudo threading in Flash has been covered in several places before, so I wrote a couple of classes to simplify the creation of what I called Micro Threads (however, I realize that Pseudo Threads might be a better name).

A MicroThread simply implements the interface IMicroThread:


package org.ncva.thrash
{
public interface IMicroThread
{
function worker(): Boolean;
function get name(): String;
}
}

The idea is that the method “worker” is executed as many times as possible during one frame after which the thread is paused to allow rendering and other AS3 execution only to be resumed again the next frame. The thread will indicate that it is finished by returning true after a call to “worker”.

My pseudo threads in contrast to those mentioned above make use of a scheduler that is responsible of calculating how many times a task can execute per frame. I’m also planning to add priority to the threads so that more important operations are given more processor time than others. Right now each task is executed at least once per frame, which can be too much if you have several heavy threads running at once. The threads should instead take turns and execute only when there is frame time available.

Task

I created a class named Task which implements the IMicroThread interface and takes as parameter a function to be used as “worker”. You can use task like this:

var x: uint = 0;

MicroThreadScheduler.scheduler.addThread(new Task(function (): Boolean {
x++;
var done: Boolean = x >= 10;
return done;
}));

Since x is in scope for the thread and the function passed to the Task is a closure, the same x will be available each time the thread is awakened, and whenever this code is reached and you create a new thread, you also create a new x, so there won’t be a conflict in the access of the variable x.

AsyncLoop

To further simplify the porting of existing code you can use the class AsyncLoop which contains some methods imitating the behavior of our beloved loop-constructs:

//  doWhile imitates "do ... while (condition)"
var x: uint = 0;
var control: uint = 0;

do {
control++;
} while (control < 10);

AsyncLoop.doWhile(function (): Boolean {
x++;
return x < 10;
});

assertEquals(control, x);

// forRange imitates the common "for (var i=0; i<L; i++)" construct
var A: Array = [1,2,3,4,5,6,7,8,9,10];
var control: uint = 0;
var sum: uint = 0;

for (var i:uint=0; i<A.length; i++)
control += A[i];

AsyncLoop.forRange(0, A.length, function (i: uint): void {
sum += A[i];
});

assertEquals(control, sum);

// forEach imitates "for each":
var A: Array = [1,2,3,4,5,6,7,8,9,10];
var control: uint = 0;
var sum: uint = 0;

for each (var x: uint in A)
control += x;

AsyncLoop.forEach(A, function (a: uint): void {
sum += a;
});
assertEquals(control, sum);

// forEach works with Object too:
var O: Object = getMyObjectFromSomeWhere();

for each (var a: Object in O)
doSomeThing(a);

AsyncLoop.forEach(O, function (a:Object): void {
doSomeThing(a);
});

The first iteration of the loop will be scheduled the next frame and following iterations will be scheduled as many times as possible the coming frames.

Thrash 0.1 0.2 is available here: [source, binary] and can be used under the terms of GNU LGPL.

No comments:

Post a Comment