程序员人生 网站导航

Web Worker在WebKit中的实现机制

栏目:互联网时间:2014-11-05 08:30:30

    web worker 是运行在后台的 JavaScript,独立于其他脚本,不会影响页面的性能。这是HTML5的1个标准;实现上讲,阅读器为wokrer启动了新的线程,从而实现了异步操作的功能;  

    下面是woker的1个简单例子,在html页面中,以worker.js为源文件,创建了名为“worker”的Worker对象,通过worker.postMessage()接口向worker线程发送消息; worker线程将JSON格式化传递的两个数据相加后,再通过postMessage()接口将消息发送回页面js运行的主线程; 主线程再在onMessage()函数中处理woker线程发来的消息; 

main.html

<html> <head> <script type="text/javascript"> var worker = new Worker('worker.js'); var obj = {"first":1, "second":2}; worker.postMessage(obj); worker.onmessage = function (event) { alert(event.data); } function postMsg(){ if (worker) worker.postMessaage(obj); } </script> </head> <body> <button onclick="postMsg()">post</button> </body> </html>


worker.js

onmessage = function (event) { var data = event.data; var first=data.first; var second=data.second; handleTask(first,second); }; function handleTask(a, b) { var out = a + b; postMessage("Worker Done! out = " + out); }


WebKit加载并履行js的流程简单分成以下几步:

1. 履行到"var worker = new Worker('worker.js')“时,在内核中构造WebCore::JSWorker对象(JSBbindings层)和对应的WebCore::Worker对象(WebCore模块); 

2.  构造JSWorker对象的进程中,根据初始化的url地址"worker.js"发起异步加载的流程; 

3.  履行worker.postMessage(),向worker线程发送JSON格式化的消息数据; 由于这个时候,worker线程还没有创建,所以消息数据放在1个临时消息队列中; 

4. worker.js异步加载完成后,创建并启动worker线程,并将临时消息队列中的消息数据copy到woker对应的WorkerRunLoop的消息队列中; 

5. worker线程创建完成后,开始处理WorkerRunLoop的消息队列中所保存的消息; 

6. woker线程发送消息到主线程; 

7. 主线程收到worker线程发送的消息,履行onMessage(); 

在经过1轮消息来回后,我们如果通过例子中button按钮来异步触发消息发送,那末步骤3的履行会有区分; 这个时候由于worker线程已创建,所以消息会直接添加到WorkerRunLoop的消息队列中; 


为了弄清楚全部实现机制,我们先来看1下WebKit内部worker相干的类, 其中Worker对应主页面JS中的'worker'对象(主线程中),而DedicatedWorkerThread表示worker线程; WorkerMessagingProxy关联了Worker对象与worker线程,从而实现主线程与worker线程之间的消息中转; DedicatedWorkerThread通过WorkerScriptController控制worker.js文件中的脚本在worker线程中的履行;阅读器会为worker线程创建1个独立的虚拟机环境(VM); 

 


下面来看1下详细流程: 

1. worker.js的异步加载进程以下图,加载是在创建WebCore::JSWorker和WebCore::Worker对象的时候发起的,并且是异步加载的流程,不会阻塞后续JS的履行,这也是为何首次调用worker.postMessage()的时候,会出现worker线程还没有创建的情况; 



2. worker线程的创建以下图所示。可以看出,当worker.js加载完成后,WebKit会通过中转对象WorkerMessagingProxy创建DedicatedWorkerThread对象,并启动WorkerThread; WorkerMessagingProxy保持DedicatedWorkerThread对象的指针; 

  


3.  主线程向worker线程发送消息的流程以下,当js履行到"worker.postMessage()“时,终究会通过JS主线程虚拟机映照到JSWorker::postMessage()函数,并通过中转对象WorkerMessagingProxy将消息添加到worker的消息队列; 如果worker线程在发送消息的时候,还没有创建,我们看到有个m_queueEarlyTasks对象会临时保存当前消息,并在worker线程创建后再转移到正式的消息队列中; 否则,直接将消息添加到WorkerRunLoop管理的正式消息队列中; 

 


4. worker线程中处理消息的流程以下,这就是worker.js中开始履行”onmessage()”的流程; 我们可以看到DedicatedWorkThread对象在自己的线程环境下的runLoop取出消息队列中的数据履行;而履行是通过EventTarget::dispatchEvent()分发并fire1个"message"类型的事件来实现的; 



5. 下图是在worker线程中发送消息的流程,也是通过WorkerMessagingProxy来进行中传,最后会触发Document::postTask()函数,该函数实际上将Document::didReceiveTask()函数抛到主线程上去履行; 


6. 下图是main线程上处理消息的流程, Document::didReceiveTask()在主线程上开始履行,终究也是通过dispatch并fire1个"message"类型的事件实现消息的处理; 

, 


所以总得来说,woker的机制是通过中转对象实现消息的传递,再通过"message"事件完成消息的处理; 

(转载请注明出处:http://blog.csdn.net/codigger)

------分隔线----------------------------
------分隔线----------------------------

最新技术推荐