ActionScript Workers Tutorial (ActionScript多线程教程)
2013-10-17 08:07
399 查看
Originaly From: http://jacksondunstan.com/articles/2401
ActionScript workers add threading to AS3 so that you can take advantage of today’s multi-core CPUs. I’ve written a couple of articles about them so far, but skipped over the basics of actually setting them up and using them. This is surprisingly tricky!
Read on for a crash course on how to use workers to speed up your app.
An ActionScript worker is embodied by the
the one you put in an HTML page) is the “main thread” and all the other threads are “worker threads”.
To create a
The first way is what I call the “all-in-one” approach. In this approach the bytes of your normal, main thread SWF are used to create the
is used to detect which of these modes the code is running as. Thankfully, the worker API makes this easy. Here’s an example:
This approach allows you to keep all of your code in one codebase, just like you’re used to with single-threaded code. Unfortunately, you’ve just mingled the main thread code with the worker thread code. So you’ll probably want to use classes to at least
keep the code in different files.
The other approach is to have to main classes: one for the main thread and one for the worker thread. You compile the worker thread’s main class, compile the main thread’s main class with the worker thread embedded, then use those embedded bytes to create
the worker thread. Here’s how that looks:
Both approaches have upsides and downsides. You may choose your approach based on whichever organization structure makes the most sense for you and your project. The only real technical difference is that the “two SWF” method will have your other AS3 code
compiled into both SWFs, thus increasing the total SWF size. However, that’s often not much of a concern these days as pure-code SWFs tend to be quite small.
The next step is to create
class represents an asynchronous communication between the threads, similar to socket communication. Most objects sent over the
the other retrieves it by that name. The two threads now use this
The main thread and the worker threads set up their
Because the threads are executing at the same time, you need to pay close attention to when the message channels are set as shared properties and when they are retrieved. You don’t want one thread to overwrite another thread’s
and you don’t want to retrieve a
To avoid this setup problem, I recommend a simple strategy: the main thread creates all of the
After the main thread has created and set its
the main thread immediately starts sending it messages the worker thread may not have even set up its event listeners on the
may be missed.
To avoid this situation I recommend a “startup”
at actually sending and receiving messages using a
You may be tempted to use the built-in
Do not be fooled! The
thread not having finished setting itself up. A “startup”
Now that you’ve safely created your worker thread established communication via a
big tasks via
of the CPU sit idle.
ActionScript workers add threading to AS3 so that you can take advantage of today’s multi-core CPUs. I’ve written a couple of articles about them so far, but skipped over the basics of actually setting them up and using them. This is surprisingly tricky!
Read on for a crash course on how to use workers to speed up your app.
An ActionScript worker is embodied by the
Workerclass. One worker represents one thread. A thread is like the code you’re used to writing, except that it runs at the same time on one of the CPU’s other cores. The code in your main SWF (e.g.
the one you put in an HTML page) is the “main thread” and all the other threads are “worker threads”.
To create a
Worker, you call the constructor and pass a SWF as a
ByteArray. This is a really strange way to instantiate a class. You cannot simply pass a function to act as the entry point of your new thread. This leads to two distinct ways of organizing your code for workers.
The first way is what I call the “all-in-one” approach. In this approach the bytes of your normal, main thread SWF are used to create the
Worker. Since the same code can be either the main thread or a worker thread, an
ifstatement
is used to detect which of these modes the code is running as. Thankfully, the worker API makes this easy. Here’s an example:
public class MainThread extends Sprite { public function MainThread() { if (Worker.current.isPrimordial) { startMainThread(); } else { startWorkerThread(); } } private function startMainThread(): void { // Use our own SWF bytes to create the worker thread var worker:Worker = WorkerDomain.current.createWorker(loaderInfo.bytes); // ... more main thread setup } private function startWorkerThread(): void { // ... worker thread setup } }
This approach allows you to keep all of your code in one codebase, just like you’re used to with single-threaded code. Unfortunately, you’ve just mingled the main thread code with the worker thread code. So you’ll probably want to use classes to at least
keep the code in different files.
The other approach is to have to main classes: one for the main thread and one for the worker thread. You compile the worker thread’s main class, compile the main thread’s main class with the worker thread embedded, then use those embedded bytes to create
the worker thread. Here’s how that looks:
//////////////// // MainThread.as //////////////// public class MainThread extends Sprite { [Embed(source="WorkerThread.swf", mimeType="application/octet-stream")] private static var WORKER_SWF:Class; public function MainThread() { var workerBytes:ByteArray = new WORKER_SWF() as ByteArray; var worker:Worker = WorkerDomain.current.createWorker(workerBytes); // ... more main thread setup } } ////////////////// // WorkerThread.as ////////////////// public class WorkerThread extends Sprite { public function WorkerThread() { // ... worker thread setup } }
Both approaches have upsides and downsides. You may choose your approach based on whichever organization structure makes the most sense for you and your project. The only real technical difference is that the “two SWF” method will have your other AS3 code
compiled into both SWFs, thus increasing the total SWF size. However, that’s often not much of a concern these days as pure-code SWFs tend to be quite small.
The next step is to create
MessageChannelobjects so that your threads can communicate with each other. This isn’t strictly necessary, but almost all multi-threaded code will need some way for the threads to collaborate. The
MessageChannel
class represents an asynchronous communication between the threads, similar to socket communication. Most objects sent over the
MessageChannelwill be copied as the threads do not usually have direct access to each others’ objects.
MessageChannelon its own isn’t very useful. A
MessageChannelobject needs to be placed into a “shared property” that both threads can access. Think of it like a drop box: one thread places a named
MessageChanneland
the other retrieves it by that name. The two threads now use this
MessageChannelto send and receives messages between them.
The main thread and the worker threads set up their
MessageChannelobjects in different ways. Here’s how it looks:
// Set up a MessageChannel to send messages from the main thread to a worker thread // Since this is called from the main thread, Worker.current is the main thread var mainToWorker:MessageChannel = Worker.current.createMessageChannel(worker); worker.setSharedProperty("mainToWorker", mainToWorker); // Set up a MessageChannel to receive messages from a worker thread into the main thread var workerToMain:MessageChannel = worker.createMessageChannel(Worker.current); workerToMain.addEventListener(Event.CHANNEL_MESSAGE, onWorkerToMain); worker.setSharedProperty("workerToMain", workerToMain); function onWorkerToMain(ev:Event): void { }
Because the threads are executing at the same time, you need to pay close attention to when the message channels are set as shared properties and when they are retrieved. You don’t want one thread to overwrite another thread’s
MessageChannel
and you don’t want to retrieve a
MessageChannelthat hasn’t been set.
To avoid this setup problem, I recommend a simple strategy: the main thread creates all of the
MessageChannelobjects and sets them as shared properties before the worker thread is started. Here’s how that process goes:
//////////////// // MainThread.as //////////////// public class MainThread extends Sprite { [Embed(source="WorkerThread.swf", mimeType="application/octet-stream")] private static var WORKER_SWF:Class; var mainToWorker:MessageChannel; var workerToMain:MessageChannel; public function MainThread() { var workerBytes:ByteArray = new WORKER_SWF() as ByteArray; var worker:Worker = WorkerDomain.current.createWorker(workerBytes); // Send to worker mainToWorker = Worker.current.createMessageChannel(worker); worker.setSharedProperty("mainToWorker", mainToWorker); // Receive from worker workerToMain = worker.createMessageChannel(Worker.current); workerToMain.addEventListener(Event.CHANNEL_MESSAGE, onWorkerToMain); worker.setSharedProperty("workerToMain", workerToMain); } private function onWorkerToMain(ev:Event): void { } } ////////////////// // WorkerThread.as ////////////////// public class WorkerThread extends Sprite { var mainToWorker:MessageChannel; var workerToMain:MessageChannel; public function WorkerThread() { // Receive from main // Since this is called from the worker thread, Worker.current is the worker thread mainToWorker = Worker.current.getSharedProperty("mainToWorker"); mainToWorker.addEventListener(Event.CHANNEL_MESSAGE, onMainToWorker); // Send to main workerToMain = Worker.current.getSharedProperty("workerToMain"); } private function onMainToWorker(event:Event): void { } }
After the main thread has created and set its
MessageChannelobjects, it’s time to start the worker thread. That’s as easy as calling
thread.start(), but how do you know when the worker thread has finished its work starting up? If
the main thread immediately starts sending it messages the worker thread may not have even set up its event listeners on the
MessageChannelobjects. In this case the messages wouldn’t be received and potentially important tasks for the worker thread
may be missed.
To avoid this situation I recommend a “startup”
MessageChannel. This
MessageChannelis, like all the other
MessageChannelobjects, created by the main thread for one purpose: to receive a message from the worker thread indicating that it’s finished starting up and is ready to be used. The following code shows how that works, including your first glimpse
at actually sending and receiving messages using a
MessageChannel:
//////////////// // MainThread.as //////////////// public class MainThread extends Sprite { [Embed(source="WorkerThread.swf", mimeType="application/octet-stream")] private static var WORKER_SWF:Class; var mainToWorker:MessageChannel; var workerToMain:MessageChannel; var workerToMainStartup:MessageChannel; public function MainThread() { var workerBytes:ByteArray = new WORKER_SWF() as ByteArray; var worker:Worker = WorkerDomain.current.createWorker(workerBytes); // Send to worker mainToWorker = Worker.current.createMessageChannel(worker); worker.setSharedProperty("mainToWorker", mainToWorker); // Receive from worker workerToMain = worker.createMessageChannel(Worker.current); workerToMain.addEventListener(Event.CHANNEL_MESSAGE, onWorkerToMain); worker.setSharedProperty("workerToMain", workerToMain); // Receive startup message from worker workerToMainStartup = worker.createMessageChannel(Worker.current); workerToMainStartup.addEventListener(Event.CHANNEL_MESSAGE, onWorkerToMain); worker.setSharedProperty("workerToMainStartup", workerToMainStartup); worker.start(); } private function onWorkerToMain(ev:Event): void { } private function onWorkerToMainStartup(ev:Event): void { var success:Boolean = workerToMainStartup.receive() as Boolean; if (!success) { // ... handle worker startup failure case } } } ////////////////// // WorkerThread.as ////////////////// public class WorkerThread extends Sprite { var mainToWorker:MessageChannel; var workerToMain:MessageChannel; var workerToMainStartup:MessageChannel; public function WorkerThread() { // Receive from main mainToWorker = Worker.current.getSharedProperty("mainToWorker"); mainToWorker.addEventListener(Event.CHANNEL_MESSAGE, onMainToWorker); // Send to main workerToMain = Worker.current.getSharedProperty("workerToMain"); // Send startup message to main workerToMainStartup = Worker.current.getSharedProperty("workerToMainStartup"); workerToMainStartup.send(true); } private function onMainToWorker(event:Event): void { } }
You may be tempted to use the built-in
WorkerStateevents to synchronize your thread startup. It’s very appealing to avoid the extra steps you see above by simply listening for the
WorkerState.RUNNINGevent like so:
// In the main thread... worker = new Worker(workerBytes); worker.addEventListener(Event.WORKER_STATE, onWorkerState); function onWorkerState(ev:Event): void { if (worker.state == WorkerState.RUNNING) { worker.start(); // ... start using the worker } }
Do not be fooled! The
RUNNINGstate only means that the
Workerhas been created and begun executing its constructor. It may or may not have finished its constructor, so you will most likely get intermittent problems due to the worker
thread not having finished setting itself up. A “startup”
MessageChannelavoids this possibility and ensures a smooth thread startup.
Now that you’ve safely created your worker thread established communication via a
MessageChannel, you’re ready to start actually using the thread. This part is totally up to your application, but in general you want to hand the worker thread
big tasks via
MessageChannel.send()and then receive the results via
MessageChannel.receive(). The possibilities are endless here, so let your imagination run wild. Just remember, if you don’t use any worker threads you are potentially letting half (dual core), three-quarters (quad core), or even seven-eighths (octo-core)
of the CPU sit idle.
相关文章推荐
- flash 系统字体显示问题
- 比较完整简洁的Flash处理XML文档数据教程 上篇第1/3页
- Flash ActionScript 中按钮和电影剪辑的事件和方法
- flash 编程习惯 小结
- flash as2.0组件样式修改方法(直接改元件就行喽)
- FLASH自动判断域名然后转向等操作
- Flash简单加密 限制 Flash 在指定域名/网址中播放的方法
- 在网页中怎样给已发布的Flash添加链接的方法
- FLASH 多参数的URL地址(带&符号)
- Flex Flash的关系分析
- Flash 与 html 的一些实用技巧
- 帮助你学习 Flash / ActionScript的12个网站
- div flash firefox div层总是被flash层遮盖
- 解决div被flash挡住的设置方法
- 解决鼠标在 flash 链接上不停闪动的问题(web页面中)
- flash 打开本地文件代码
- Flash、AS入门的正确途径解析
- Flash影片中的图片抖动锯齿解决方法
- Ubuntu解决Flash安装问题
- kali之flash挂马