Concurrent Programming and Parallel Patterns
Concurrent Programming Patterns
If you’ve viewed the posts that we’ve had on both parallel (//P) and concurrent (||P) programming, most of what you’ve seen has been long-standing attempts at borrowing what’s been published on parallel programming and applying it to ActionScript 3.0. The examples originated in other languages, especially in C#, and attempting to simulate parallel programming in AS3 has been more hope than reality. However, now that all of the development software has been made available (at least in Adobe Labs), you can write honest-to-goodness ||P programs with ActionScirpt 3.0. You’ll need Flash Builder 4.7 or newer, and the latest Flash player. (I have not tried this with Flash CS6 yet, but feel free to do so and let me know how it works out.) At this time, the concurrent programming features are only available for desktop AIR 3.4+; so forget about using workers for developing mobile apps at this point in time.
Why Concurrent Programming?
Design patterns in ActionScript 3.0 (or any other language) are not for speeding up programs. They are for speeding up development time. Re-use is the key to understanding and effectively using design patterns. However, concurrent programs are decidedly for speeding up programs. A task is given to more than one worker (a Primordial Worker and Background Worker(s)). By having two processes running simultaneously, the program should run faster. (For more details on how Adobe has implemented this model see the Concurrent Graphic Novel on this blog and anything written by Thibault Imbert on the topic.)
Despite the fact that design patterns in computer programming are associated with speeding up development time, the design patterns in Design Patterns for Decomposition and Coordination on Multicore Architectures by Colin Campbell, Ralph Johnson, Ade Miller, and Stephen Toub are all about using parallel programming (in C#) for writing programs with certain built-in features that use C# statements that call parallel operations. (See the chapter on Parallel Loops.) For example, C# has statements like Parallel.For() and Parallel.ForEach().
In ActionScript 3.0, we do not have a set of for statements to use with ||P, but we do have statements that will allow us to speed things up by dividing a single task into multiple ones that are handled simultaneously. While AS3 statements are different than those used with //P, we can go ahead and make concurrent versions of parallel programming design patterns. The place to start is with with Parallel Loops. (See Parallel Loops: The First Multicore Design Pattern on this blog.) Essentially, a Concurrent Loop is the same as a Parallel Loop in that a single task is broken down into two (or more) tasks and handled simultaneously. By “broken down” I refer to decomposition. (See the post on decomposition on this blog for more details.) However, instead of decomposition breaking down tasks handled by separate cores, in concurrent programming in AS3, they’re handled by separate workers.
An Example of a Concurrent Loop Using Workers
In previous attempts at creating simultaneously running loops in ActionScript 3.0, I could not get the speed advantage because of the Rube Goldberg contraptions I constructed. So the first challenge is to create a loop and first run it with two or more workers acting concurrently and compare it with a single loop with no concurrent component. If everything went according to plan, two concurrent loops could process a set number of iterations twice as fast as a single loop. After setting up a timer and some helper classes, I tested it, and it worked. I set up a loop with 200 million iterations. The concurrent loop was handled by two workers, each with 100 million iterations. After both workers were done, a timer would show how long the operation took. The same was done with a single loop using the same timer. It took twice as long. Click on the Play button to see. (You can download all of the source code and helpers as well.)
When you click on the Play button, you can start the concurrent loop and single loop separately. You’ll see that the concurrent loop and make the same number of iterations in about half the time of the single loop.
Two Workers; One Task
The LoopTest class is embarrassingly long (mainly formatting), but the core of the class is in the setWorkerChannels() method. By default, the current Worker is the primordial worker, and it can create any number of background workers. The primordial worker is created by default, and it then creates a message channel to the background worker(s). In this example, workerB is the background worker. The background worker is declared as an instance of the Worker class and instantiated in the context of the primordial worker using a ByteArray() object (byteMeArray.)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | package { import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; import flash.system.MessageChannel; import flash.system.Worker; import flash.system.WorkerDomain; import flash.system.WorkerState; import flash.text.TextField; import flash.utils.ByteArray; import uifactory.* public class LoopTest extends Sprite { private var workerB:Worker = null; private var backToMain:MessageChannel; private var mainToBack:MessageChannel; //UI package private static var display:UICreator; private static var displayNow:TextField; private static var btn:UICreator; private static var btnNow:Sprite; private static var btnS:UICreator; private static var btnNowS:Sprite; //Timer private var timeKeeper:TimeKeeper=new TimeKeeper(); private var expense:Number; public function LoopTest() { displaySetup(); setWorkerChannels(); } private function setWorkerChannels():void { var byteMeArray:ByteArray = new ByteArray(); byteMeArray = this.loaderInfo.bytes; if (Worker.current.isPrimordial) { //Worker.current is primordial worker here workerB = WorkerDomain.current.createWorker(byteMeArray); mainToBack = Worker.current.createMessageChannel(workerB); backToMain = workerB.createMessageChannel(Worker.current); workerB.setSharedProperty("incoming",mainToBack); workerB.setSharedProperty("outgoing",backToMain); backToMain.addEventListener(Event.CHANNEL_MESSAGE, mainMessageHandler); workerB.start(); } else { //Worker.current is background worker here mainToBack = Worker.current.getSharedProperty("incoming"); backToMain = Worker.current.getSharedProperty("outgoing"); mainToBack.addEventListener(Event.CHANNEL_MESSAGE,backgroundMessageHandler); } } private function mainMessageHandler(e:Event):void { if (e.target.messageAvailable == true) { var loopSize:uint = e.target.receive(); if(loopSize==86) { displayNow.appendText("Background Worker done\n"); expense=timeKeeper.endTime() displayNow.appendText("Elapsed time:"+String(expense)); } for(var l2:uint=100000000;l2 < loopSize;l2++) { if(l2>=loopSize-1) { displayNow.appendText("Primordial Worker done\n"); } } } } private function backgroundMessageHandler(e:Event):void { if (e.target.messageAvailable == true) { var loopSize:uint = e.target.receive(); backToMain.send(200000000); for(var l1:uint=0;l1 < loopSize;l1++) { if(l1>=loopSize-1) { backToMain.send(86); } } } } private function sendMsg(e:MouseEvent):void { displayNow.appendText("Starting both loops:\n\n"); timeKeeper.startTime(); mainToBack.send(100000000); } private function singleLoop(e:MouseEvent):void { displayNow.appendText("\n\nStarting single loop:\n\n"); timeKeeper.startTime(); var singleSize:uint=200000000; for(var sl:uint=0;sl < singleSize;sl++) { if(sl>=singleSize-1) { singleTime(); } } } private function singleTime():void { expense=timeKeeper.endTime(); displayNow.appendText("Elapsed time:"+String(expense)+"\n\n"); } private function displaySetup():void { btn = new SpriteButtonMaker(); btnNow=btn.factoryMethod("Start Concurrent Loop",10,10,0xBFA473,180,40); btnNow.addEventListener(MouseEvent.CLICK,sendMsg); addChild(btnNow); btnS = new SpriteButtonMaker(); btnNowS=btnS.factoryMethod("Start Single Loop",10,60,0xBFA473,180,40); btnNowS.addEventListener(MouseEvent.CLICK,singleLoop); addChild(btnNowS); display=new TextDisplayMaker(); displayNow = display.textFactoryMethod("DYNAMIC",10,120,0xD5E7C2,400,200); addChild(displayNow); } } } |
Note: All of the other classes used for timing and formatting in this example are available in the download package and will not be discussed here. However, you might want to open them so you can see how they’re related to the LoopTest class.
As an OOP process, creating and programming with workers is very natural in the sense that most of the work is setting up and using message channels and shared properties. It’s a matter of objects communicating with one another. Within the primordial worker context, the background worker sends it first message when the Worker.start() method. Once the background worker starts working, it’s state (status) is shared with the primordial worker through messaging and shared properties and vice versa.
Is a Concurrent Loop a Design Pattern?
The issue of whether or not the Concurrent Loop (conceptually the same as a Parallel Loop) is a design pattern because Colin Campbell, Ralph Johnson, Ade Miller, and Stephen Toub say it is stands at the crux of the issue. In C#, the built-in statements for parallel loops somehow look less like design patterns and more like statements and nothing more. (See the Parallel Loops post on this blog.) Rather than having to build a concurrent loop as is done in this post’s example, if all that was required was a concurrent loop statement such as Concurrent.for(), I’m not sure what it would be. Certainly some languages have built-in design patterns, and so because an operation is built into the language does not preclude it from being a design pattern. Anyway, it is certainly the stuff for discussion, and I hope to hear from some of you who have opinions and ideas on this new ActionScript 3.0 arena.

Bill Sanders
Looks great, thanks for putting this together!
Quick comment – AIR (desktop) does support workers as of 3.4, though this does seem a bit buggy still. See AS3 ‘Worker’ documentation. Not sure why Adobe’s release notes say that Workers are just “Flash Player only” though…
Hi Andrew.
You are correct! My bad. I now think AIR = Mobile app development, but AIR 3.4 desktop does accept workers.(I fixed it in the write-up. I thank you.)
Kindest regards,
Bill
Hello William, thank you for the great write up on workers. I had a question on one part, is the StartConcurrentLoop actually running 3 million times instead of 2 million? It looks like the worker is doing 1mil operations and then it is doing backToMain.send with 2mil operations, is that 3mil total? Thank you
Oh my bad, I didnt notice that the loop already started at 1 mil.
Hi Doug,
Glad you caught it before I went nuts! Both the worker (primordial) and the background worker share in the task, and it actually works!
Let me know if you can use it in a practical app!
Kindest regards,
Bill