JavaScript Canvas - Web Workers |
Written by Ian Elliot | ||||
Tuesday, 07 June 2022 | ||||
Page 3 of 3
Transferable ObjectsThe way that worker threads avoid many of the problems of multi-threading is by limiting the way threads can interact. They can only communicate by sending messages and any data that is included in the message isn't shared, but copied. This means that each thread is always working on its own data and no object can be used by more than one thread at a time. Copying data is safe, but if the data is large it can be slow. An alternative is to use transferable objects. These are not copied, but even so only one thread can access them at any given time. The idea is that the object belongs to just one thread and only that thread can access the object. Ownership of the thread can be passed to another thread by the object being included in a message sent from the current owner to another thread. Transferable objects are only supported in the latest browsers. The mechanism only applies to four types of object - ArrayBuffer, MessagePort, ImageBitMap and OffscreenCanvas. This makes sense because these are all potentially large objects which are best not passed by copy. To send a transferable object you simply follow the usual data object in postMessage by an array of transferable objects. The way that transferable objects work is simple, but it can be confusing. As usual, you pass data using the message object which becomes the data property of the event object passed to the event handler. The data that you want to transfer has to be included either as the message object or a property of the message object. To be passed by transfer rather than clone, the object also has to be listed in the transfer array. So, for example: postMessage(object,[object]); will transfer object and the event handler will receive the object as event.data. Alternatively, you could use: postMessage({mydata: object},[object]); which will also transfer object, but the event handler will receive it as event.data.mydata. Obviously, you could pass additional properties and if these were not included in the transfer list they would be passed by cloning. Once an object has been transferred it is no longer usable in the original thread. For example in the case of an ArrayBuffer its size is reduced to zero in the original thread. Notice that once transferred its ownership cannot be simply transferred back because the reference that is passed is in event.data or event.data.myobject and not the original reference. That is, it is not a simple turning off and on of the original reference. You can arrange for this to happen, however. We need a very simple example and to do this we need to work with an ArrayBuffer. An ArrayBuffer is a raw collection of bytes and you cannot access it directly. It has to be converted into a typed array before you can access its data, see Chapter 11 for details. However, we can simply create a raw buffer and pass it back and forth between the main thread and the worker thread as an example without worrying about what data it contains: var worker = new Worker("transfer.js"); This simply creates an eight byte ArrayBuffer and transfers it to the worker thread. When the program is run you see: UI before transfer 8 indicating that the ArrayBuffer is no longer available in the UI thread. In the Worker thread we can use the ArrayBuffer in the standard way: this.addEventListener("message", function(event){ console.log("Worker got data! " + You will see the message: Worker got data! 8 indicating that the ArrayBuffer is now available in the worker thread. The web worker can pass the ArrayBuffer back to the UI thread: this.addEventListener("message",function(event){ After the data has been transferred back to the UI thread you will once again see that the length of the ArrayBuffer is 0. In the UI thread the data can be retrieved in the usual way: worker.addEventListener("message", In this case you will see that the event.data is an ArrayBuffer of eight bytes and the original arrayBuf is still 0, that is the original transferred data is not restored. You can, of course, restore the reference to the original data: worker.addEventListener("message", Now it looks as if the original data has been handed back by the worker thread. The big problem with transferring data is that the thread that owned it originally doesn't get the use of it, not even a copy, while the other thread is using it. This doesn't matter as long as the initial owner is generating the data for the first time. For example, if a worker thread is downloading a resource it can transfer it to the UI thread to be used with no problems. Compare this to a Worker thread that is modifying, rather than originating, the data. If the UI thread passes the worker thread a bitmap or an OffscreenCanvas to process, what does the UI thread show while the worker has ownership? In book but not in this extract
Summary
Now available as a paperback or ebook from Amazon.JavaScript Bitmap Graphics
|
||||
Last Updated ( Tuesday, 07 June 2022 ) |