ActionScript Workers: Condition Class Example
The Condition
class that debuted alongside the Mutex
class in Flash Player 11.5 is very useful but much less understood. Unfortunately, Adobe’s documentation doesn’t include a usage example and there seem to be none available elsewhere on the internet. So today’s article provides an example of how to use the flash.concurrent.Condition
class.
The Condition
class contains a Mutex
which it uses to control a “resource”, just like the plain usage of Mutex
. However, it adds functionality to Mutex
such as the ability to notify multiple workers that you’re done with the resource without any one of them needing to take exclusive control of the Mutex
. This can simplify coordination between workers and help you avoid subtle bugs.
The following example shows a very simple usage of the Condition
class. The main thread/worker creates a ByteArray
and uses a worker thread to compress it. You might do this in order to maintain UI responsiveness on the main thread while compressing some data for upload to a server, for example. A Condition
is used to control access to the shared ByteArray
resource. The code is thoroughly documented and minimal, so hopefully you’ll be able to follow the flow of control between the two workers.
package { import flash.concurrent.Mutex; import flash.concurrent.Condition; import flash.display.Sprite; import flash.text.TextField; import flash.text.TextFieldAutoSize; import flash.system.Worker; import flash.system.WorkerDomain; import flash.utils.ByteArray; /** * Test to show the usage of the Condition class * @author Jackson Dunstan (JacksonDunstan.com) */ public class ConditionTest extends Sprite { /** * Start the app in main thread or worker thread mode */ public function ConditionTest() { // If this is the main SWF, start the main thread if (Worker.current.isPrimordial) { startMainThread(); } // If this is the worker thread SWF, start the worker thread else { startWorkerThread(); } } /** * Start the main thread */ private function startMainThread(): void { // Create a Condition that we'll wait on until the worker thread is // done with its work of compressing our ByteArray. The Condition's // mutex is initially locked so we can unlock it only when we're // ready for the worker to start compressing. var conditionMutex:Mutex = new Mutex(); conditionMutex.lock(); var condition:Condition = new Condition(conditionMutex); // Create a 32 megabyte ByteArray for the worker to compress const SIZE:uint = 1024*1024*32; var bytes:ByteArray = new ByteArray(); bytes.length = SIZE; bytes.shareable = true; // Create the worker from our own SWF bytes var swfBytes:ByteArray = this.loaderInfo.bytes; var worker:Worker = WorkerDomain.current.createWorker(swfBytes); // Share the bytes and condition with the worker worker.setSharedProperty("bytes", bytes); worker.setSharedProperty("condition", condition); // Start the worker worker.start(); // Unlock the Condition's mutex and suspend the main thread until // the worker thread is done compressing the ByteArray. At that // point it will call notify() on the Condition which will resume // this thread right after its call to wait(). condition.wait(); // Output some statistics about the work that was done var logger:TextField = new TextField(); logger.autoSize = TextFieldAutoSize.LEFT; logger.text = SIZE + " bytes compressed to " + bytes.length + " bytes (" + ((bytes.length*100.0)/SIZE).toFixed(2) + "%) "; addChild(logger); } /** * Start the worker thread */ private function startWorkerThread(): void { // Get the bytes to work on and the Condition that controls access // to the bytes var bytes:ByteArray = Worker.current.getSharedProperty("bytes"); var condition:Condition = Worker.current.getSharedProperty("condition"); // Lock the condition mutex. This will suspend the worker until the // main thread calls wait() on the condition. condition.mutex.lock(); // We have locked the mutex, which is our signal that resource it // represents is available to be worked on. In this case, that means // that the ByteArray has been set up and is ready to be compressed. bytes.compress(); // We've finished our work on the ByteArray, so we can notify the // main worker that it's ready. Note that calling notify() does not // release the lock on the Condition's mutex condition.notify(); // Unlock the mutex so other workers can use the shared ByteArray condition.mutex.unlock(); } } }
Spot a bug? Know of other examples of Condition
or want to provide your own? Post a comment!
#1 by alebianco on December 9th, 2013 ·
condition.wait();
this means that the main UI thread is being stopped to wait the other worker to finish? isn’t it the opposite of what we want to achieve by using workers?
#2 by jackson on December 9th, 2013 ·
Yes, that is what’s happening in this case. Yes, it’s the opposite of what you want to achieve. I implemented it this way for the sake of keeping this example small and straightforward. In a real-world scenario you may have a much more complicated scenario, such as having one worker generating
ByteArray
objects and another worker compressing them.#3 by hivaga on December 9th, 2013 ·
Great WORK !!! I adore your tests :) Interesting how condition approach compare to “timeout + mutex.trylock()”. Condition vs “timeout mutex lock” and maybe vs “channel messaging”. But the benchmark must include also CPU usage + optimal speed, because for sure messaging has little to none CPU overhead compared to “timeout + mutex.trylock()”. The idea is to build best approach for faster notification when shared ByteArray have been updated.
#4 by Dawson Valdes on February 19th, 2014 ·
I use it to wait for a shared bytearray to have something in it before reading from it . It use it for flow control so I do not need to lock the bytearray constantly in a loop to check if it has bytes available.
Worker thread lock, if bytearray bytes not available then unlock and wait , lock,read, unlock.
Main thread lock, write bytes to bytearray , notify, unlock.
#5 by Benjamin Guihaire on April 27th, 2014 ·
Your example gave me an idea, I tried to create a “fake” synchronous system to download “things” using urlstream & urlrequest
The idea was to implement something that exists in c# HttpWebRequest.GetResponse which gives synchronous results.
So instead of compressing a byteArray in a worker thread, from your example, I modified the example to send a URL string to the worker thread , and wait by calling condition.wait() .. and it should resume once my worker thread is done with the URLRequest.
Long story short .. this doesn’t work ! When the main thread is waiting, URL requests on the worker thread are never processed ! I verified that my worker thread is running by adding an enterFrame event with some traces ..
Also, note that once I remove the condition.wait() from the main thread, my URL request on the worker thread are working.
It looks like its impossible in flash to do what I tried to accomplish (regardless of if the idea of doing something like that was good or bad), if the main thread is “stopped”, all the URL Requests are stopped
#6 by jackson on April 27th, 2014 ·
Does it work if you synchronize another way, such as with a
Mutex
orMessageChannel
?Mutex
can lead to very similar, synchronous-looking code and might be a good alternative.#7 by Benjamin Guihaire on April 29th, 2014 ·
so I tried with messages, mutex etc… I even tried to do something very hacky from the main thread to wait for the byteArray length by the URL Streamin .. to be changed while(bytes.length<=0); … no matter what I do, if the main thread is "stopped" , or stuck in some action script, no URLRequest work from the worker thread..
#8 by Benjamin Guihaire on April 30th, 2014 ·
we can see in adobe-scout that all IO are processed between frames … I suspect all IO operations are performed in the main-thread, regardless of where (which thread) they are initiated.
#9 by Benjamin Guihaire on May 6th, 2014 ·
it works with sockets.
#10 by zermok on July 13th, 2018 ·
Be aware that shareable bytearray has been deactivated since Flash 30
https://www.adobe.com/go/fp-spectre