Original link: Http://esdot.ca/site/2012/intro-to-as3-workers-hello-world
With the beta release of AIR3.4 and Flash Player11.4, Adobe has finally launched the most demanding API for many years: Multithreading!
Today, using AS3 workers makes it easy to create real multithreaded applications with just a few lines of code. The API is fairly straightforward, and they do something very useful: bytearray.shareable, for example, can share memory between workers, and a new The Bitmapdata.copypixelstobytearray method is used to quickly convert BitmapData to ByteArray.
In this article, I'll go through all the parts of the API workers. Then we'll look at a simple applet: Helloworker.
If you want to do this, you can download the following Flash Builder project files first:
Flashbuilder Project file
If you just want to see the code, the nonsense is not much to say, download this look: hellworldworker.as
What is that worker?
Simply put, a worker is another SWF program that runs in your main SWF. For more in-depth understanding, please visit: Thibault Imbert this article New Wen.
To create a worker, you need to call the WorkerDomain.current.createWorker () method and pass in a byte stream of a SWF file.
There are 3 ways to generate those SWF byte streams.
1. Use the Loaderinfo.bytes property of the main SWF application itself, and then judge the Worker.current.isPrimordial flag in the constructor of the document class (true means that the main thread is currently). This is the quickest way to create workers and has an advantage in the immediate debugging cycle.
2. Publish a SWF file and embed it into your main program using the [Embed] tab. This way there will be tools in Flash Builder4.7, but it's still too inflexible before the tools come out. Every time you change the code in the worker, you have to re-export the SWF, which can quickly get you bored!
Using the worker from class, this new class library allows you to create workers directly from the class. This may seem like the best solution, but it allows you to introduce a series of external dependencies into your project.
In this initial tutorial, I will focus on the first way, using my own loaderinfo.bytes byte stream. This is a quick but flawed way. In the next tutorial, I'll try the "Worker from class" library approach.
For the 2nd way, there is a very good tutorial that you can take a look at Lee Brimelow's video tutorial Part 1 and Part 2.
Let's talk.
Communication is critical in any multithreaded usage scenario. The cost of transferring memory data directly from one thread to another is expensive, and memory sharing requires careful design and thoughtful consideration. Most of the challenges in implementing worker systems come from looking for a right design architecture that allows data to be shared among your workers.
To help us solve this problem, Adobe has given us some simple (but flexible) ways to send data.
(1) Worker.setsharedproperty () and Worker.getsharedproperty ()
This is the simplest way to pass data, but with the most limited functionality. You can call Worker.setsharedproperty ("key", value) to set the data, and then use WorkerDomain.current.getSharedProperty ("key") on the other side to get them. Example code:
?
12345 |
//在主线程里 worker.setSharedProperty( "foo" , true ); //在worker线程里 var foo: Boolean = Worker.current.getSharedProperty( "foo" ); |
You can store simple or complex objects here, but for most cases the stored data is serialized, and it is not really shared. If a data object is changed on one side, the update is not synchronized on the other side until it is updated after you call the Set/get method again.
There are two exceptions that can be shared if you pass an object that is ByteArray and the Shareable property is true, or is a Messagechannel object. Coincidentally, these are just the other two methods of communication
(2) Messagechannel
Messagechannels is like a worker to another one-way conduit. They combine an event mechanism with a simple queue system. You call the Channel.send () method at one end, and a Event.channel_message event is thrown at the other end. In the event handler function, you can call Channel.receive () to receive the data sent over. As I mentioned, this method is similar to a queue. So, you can send () or receive () multiple times on both sides. Example code:
?
123456789101112131415161718 |
//在主线程里
mainToWorker.send(
"ADD"
);
mainToWorker.send(
2
);
mainToWorker.send(
2
); //在worker线程里
function onChannelMessage(event:Event):
void {
var msg:
String = mainToWorker.receive();
if
(msg ==
"ADD"
){
var val1:
int = workerToMain.receive();
var val2:
int = workerToMain.receive();
var result:
int = val1 + val2;
//发送计算结果给主线程
workerToMain(result);
}
}
|
The Messagechannels object uses Worker.setsharedproperty () for sharing, so it is possible to share it among the many workers. But they can only have one send endpoint. In this way, it seems good to name the contract with their recipient. such as Channeltoworker or Channeltomain.
Example code:
?
12345678910 |
//in mainline thread workertomain = Worker.createmessagechannel (worker.current); worker.setsharedproperty ( maintoworker = Worker.current.createMessageChannel (Worker); worker.setsharedproperty ( //on worker line thread workertomain = Worker.current.getSharedPropert ( "Workertomain" ) ; maintoworker= Worker.current.getSharedPropert ( " Maintoworker " |
There is an important limitation for both Messagechannel and sharedproperties communication methods, which are serialized when the data is sent. This means that they need to be parsed, transmitted, and then restored at the other end. The performance cost of this is relatively large. Due to this limitation, the two modes of communication are best applied to the interstitial transmission of small-scale data.
What if you really want to share a huge chunk of data? The answer is to use shared bytearray.
(3) Bytearray.shareable
The quickest way to transfer data is not to transmit it at all!
Thanks to Adobe for giving us the means to share ByteArray objects directly. This is very powerful because we can store almost any data in the ByteArray. To share a ByteArray object, you need to set bytearray.shareable=true and then call Messagechannel.send (ByteArray) or Worker.setsharedpropert (" ByteArray ", ByteArray) method to share it.
Once your ByteArray object is shared, you can write it directly to it, and then read directly from it on the other end, great.
Basically this is all the way of communication. As you can see, there are 3 different ways to share data. You can use them together in a variety of ways. Already done with the infrastructure, let's take a look at a simple Hello World example.
Sample Application
First of all, in order for your project to compile and run correctly, you need to do the following:
1. Download the latest AIR3.4 SDK and PLAYERGLOBAL.SWC files and make sure to configure them in your project.
2. Add the compilation parameters: "Swf-version=17" to your project properties.
3. Install the Flashplayer 11.4 Standalone debugger version.
By doing the above steps, you should now be able to see various worker-related APIs such as Worker,workerdomain,messagechannel and so on. If you're having trouble with steps, refer to Lee Brimelow's video tutorial, the first few Zhongyu to introduce this.
You can also download a copy of my Flash Builder project directly, which should be able to run.
First Step: Document class
First, we need to create our document class:
?
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647 |
public class HelloWorldWorker
extends Sprite{
protected var mainToWorker:MessageChannel;
protected var workerToMain:MessageChannel;
protected var worker:Worker;
public function HelloWorldWorker()
{
/**
* 启动主线程
**/
if
(Worker.current.isPrimordial){
//从我们自身的loaderInfo.bytes创建
worker = WorkerDomain.current.createWorker(
this
.loaderInfo.bytes);
//为两个方向通信分别创建MessagingChannel
mainToWorker = Worker.current.createMessageChannel(worker);
workerToMain = worker.createMessageChannel(Worker.current);
//注入MessagingChannel实例作为一个共享对象
worker.setSharedProperty(
"mainToWorker"
, mainToWorker);
worker.setSharedProperty(
"workerToMain"
, workerToMain);
//监听来自Worker的事件
workerToMain.addEventListener(Event.CHANNEL_MESSAGE, onWorkerToMain);
//启动worker(重新实例化文档类)
worker.start();
//设置时间间隔给worker发送消息
setInterval(
function
(){
mainToWorker.send(
"HELLO"
);
trace
(
"[Main] HELLO"
);
},
1000
);
}
else
{
//在worker内部,我们可以使用静态方法获取共享的messgaeChannel对象
mainToWorker = Worker.current.getSharedProperty(
"mainToWorker"
);
workerToMain = Worker.current.getSharedProperty(
"workerToMain"
);
//监听来自主线程的事件
mainToWorker.addEventListener(Event.CHANNEL_MESSAGE, onMainToWorker);
}
}
}
|
Follow these steps step by step:
1. We use the Worker.current.isPrimordial attribute to distinguish whether we are the main thread or worker.
2. In the main thread, we create the Messagechannels object and share it with the worker.
3. We start the worker thread, which causes the document class to be instantiated again.
4.worker is created, and then gets a reference to the shared Messagechannels object.
I also set a small loop: Send a "HELLO" string message to the worker every 1000ms. Next, we'll have the worker return a "world" to the main thread and print it out.
Step Two: Handle events
Now that we have a shared messagechannels object, we can easily communicate between the worker. As you can see in the code above, we have set the event listener to the object reference. So the rest is to create them:
?
1234567891011121314 |
//从主线程接收信息
protected function onMainToWorker(event:Event):
void {
var msg:* = mainToWorker.receive();
//当主线程给我们发送"HELLO"时,我们返回"WORLD"
if
(msg ==
"HELLO"
){
workerToMain.send(
"WORLD"
);
}
}
//从worker线程接收信息
protected function onWorkerToMain(event:Event):
void {
//打印输出worker里接收到的任何消息
trace
(
"[Worker] " + workerToMain.receive());
}
|
If you run this program now, you will see that every 1000ms "HELLO" and "world" are printed once. Congratulations on completing your first multithreaded application!
Step three: A little more?
Well, you have to admit, this app looks a little bit useless. Let's take a little step closer to our test program and let it do some math. How's that?
First, let's modify the interval function so that it looks like this:
?
1234567 |
//设置一个时间间隔让Worker线程做一些数学计算
setInterval(
function
(){
mainToWorker.send(
"ADD"
);
mainToWorker.send(
2
);
mainToWorker.send(
2
);
trace
(
"[Main] ADD 2 + 2?"
);
},
1000
);
|
We then modify the event listener function in the worker to get these values:
?
12345678910 |
protected function onMainToWorker(event:Event):
void {
var msg:* = mainToWorker.receive();
if
(msg ==
"ADD"
){
//接收到两个值,然后把它们相加
var val1:
int = mainToWorker.receive();
var val2:
int = mainToWorker.receive();
//返回计算结果给主线程
workerToMain.send(val1 + val2);
}
}
|
That's it! If you run this program now, you will see "[Main] Add 2 + 2?" and then the correct answer "4″ will be output."
If you want to see the full class.
Click here: hellworldworker.as
In the next tutorial, we'll step by step to see how we can use multithreading to do some image processing.
From: http://blog.domlib.com/articles/319.html
AS3 Multithreading QuickStart (i): Hello world[translation]