web worker在WebKit中的实现机制

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

    下面是woker的一个简单例子,在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线程还没有创建,所以消息数据放在一个临时消息队列中; 

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

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

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

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

当然如果,在经过一轮消息来回后,我们通过例子中的button按钮来异步触发消息发送,那么步骤3的执行会有区别; 这个时候因为worker线程已经创建,所以消息会直接添加到WorkerRunLoop的消息队列中; 


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

 


下面来看一下详细流程: 

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()分发并fire一个"message"类型的事件来实现的; 



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


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

, 


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


----------------------------------------------------------------------

原文地址:http://blog.csdn.net/codigger/article/details/40581343

----------------------------------------------------------------------

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。