Saturday, August 4, 2012

Intro to AS3 Workers: Hello World

With the beta release of AIR 3.4 and Flash 11.4, Adobe has introduced one of the most requested API’s for years: Multi-threading!
With AS3 workers it’s now very easy to create true multi-threaded applications with just a few lines of code. The API is fairly straightforward, and they’ve done some very handy things like a new ByteArray.shareable property to share memory between worker’s, and a new BitmapData.copyPixelsToByteArray API for quickly converting bitmapData to ByteArray.
In this post I’ll go through the various components of the Worker API, and we’ll look at a simple little HelloWorker application.
If you would like to follow along, you can download the FlashBuilder Project Files:

So what is a worker?

Put simply a worker is just another SWF that’s running alongside your main SWF. For a an in depth background check out Thibault Imbert’s excellent writeup.
To create a worker you call WorkerDomain.current.createWorker()  function and pass it the bytes of a SWF.
There’s currently 3 ways to generate these SWF bytes:
  1. Use the loaderInfo.bytes of the main SWF, and check the Worker.current.isPrimordial property inside your document class’s constructor. This is the quickest way to create a worker, and has the advantage of instant testing-debug cycle.
     
  2. Publish a SWF file, and [Embed] it in your main project. This will have tooling support in FlashBuilder 4.7, but until then it’s quite cumbersome. Everytime you change the code in your worker, you must re-export the SWF, this gets annoying pretty quick!
     
  3.  Use Worker From Class a new Library which allows you to create workers directly from Classes. This looks like the most optimal solution right now, but does  introduce a couple of external dependencies in your project.
     
In this initial tutorial I’m going to focus on the first method, using our own loaderInfo.bytes. This is the quick and dirty way to do it. In a follow up tutorial I’ll take a look at Worker From Class.
For a great tutorial on method #2 check out Lee Brimelow’s video’s part 1 and part 2.

Lets talk.

In any multi-threaded scenario communication is key. Transferring memory from one thread to another is expensive, and sharing memory takes careful planning and consideration. Much of the challenge in implementing a worker system comes from finding the right architecture for sharing data to and from your workers.
To help us out Adobe has given us a few simple (but flexible) ways to send messages.

worker.setSharedProperty() / worker.getSharedProperty()

This the simplest but most limited way to pass values around. You can call worker.setSharedProperty(“key”, val) to set things, and WorkerDomain.current.getSharedProperty(“key”) to get them on the other side. You can store both simple and complex objects here, but for most cases the data is serialized, it’s not actually shared. If a value changes on one end, it will not be updated on the other until you call share/get again.
The exception is if you pass a ByteArray with byteArray.shareable=true, or a MessageChannel. Coincidentally, these are the other two methods of communication :)

MessageChannels

MessageChannels are like one way conduits from one worker to another, and they’re event based. So, you will call channel.send() on one end, and channel.receive() on the other. To know when a new message has arrived, you just listen on the channel for Event.CHANNEL_MESSAGE. Messages are queued up and received in the order they were sent.
MessageChannels are shared using worker.setSharedProperty(), and can be shared throughout as many workers in your Application as you would like, but they only ever have one destination. As such, it seems to be a good convention to name them by their receiver, ie channelToWorker or channelToMain
An important limitation to both MessageChannel and sharedProperties, is that the data is serialized when it is sent. That means it needs to be deconstructed, transferred, and reconstructed on the other side. This can be costly. Due to this limitation, these API’s are best used for transfering small amounts of data intermittently. Ideally just simple strings or numbers.
So what if you do need to share a huge chunk of data? The answer is a shareable ByteArray…

byteArray.shareable

The fastest way to transfer data is to not transfer it at all!
Thankfully Adobe has given us the ability to share a ByteArray directly. Since we can store virtually anything inside a ByteArray, this is extremely powerful. To share a byteArray, you set  byteArray.shareable=true and then use messageChannel.send(byteArray) or worker.setSharedPropert(“byteArray”, byteArray) to share it.
Once your byteArray is shared, you can write into it, and read from it on the other end instantly. Beautiful :)
That is basically all there is to it. As you can see, there are 3 distinct ways to share data, and you could combine them in many many different ways. Now that we’ve gone over the structure, lets look at a simple Hello World.

Sample Application

First, in order to get your projects compiling, you’ll need to do a couple things:
  • Download the latest AIR 3.4 SDK, or playerglobal.swc file, make sure your project is using them.
  • Add compile flag “swf-version=17″ to your project
  • Install FlashPlayer 11.4 standalone debugger
With that, you should now see the various Worker related API’s such as Worker, WorkerDoman, MessageChannel etc. If you’re having trouble with this, check out the first couple minutes of Lee Brimelow’s video where he walks through setup.
You can also just download a copy o my FlashBuilder Project which should just work.

Step 1 – The Document Class

First we’ll need to create our document class: [...]
Read more: Intro to AS3 Workers: Hello World

No comments:

Post a Comment