自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

深入HTML5應(yīng)用實(shí)踐:多線程編程

開發(fā) 后端 前端
W3C 在 HTML5 的規(guī)范中提出了工作線程(Web Worker)的概念,工作線程允許開發(fā)人員編寫能夠長時(shí)間運(yùn)行而不被用戶所中斷的后臺程序, 去執(zhí)行事務(wù)或者邏輯,并同時(shí)保證頁面對用戶的及時(shí)響應(yīng)。本文深入 HTML5 多線程規(guī)范,講述多線程實(shí)現(xiàn)原理、方法,同時(shí)以實(shí)例的形式講解 HTML5 中多線程編程以及應(yīng)用。

HTML5 中工作線程(Web Worker)簡介

至 2008 年 W3C 制定出第一個(gè) HTML5 草案開始,HTML5 承載了越來越多嶄新的特性和功能。它不但強(qiáng)化了 Web 系統(tǒng)或網(wǎng)頁的表現(xiàn)性能,而且還增加了對本地?cái)?shù)據(jù)庫等 Web 應(yīng)用功能的支持。其中,最重要的一個(gè)便是對多線程的支持。在 HTML5 中提出了工作線程(Web Worker)的概念,并且規(guī)范出 Web Worker 的三大主要特征:能夠長時(shí)間運(yùn)行(響應(yīng)),理想的啟動(dòng)性能以及理想的內(nèi)存消耗。Web Worker 允許開發(fā)人員編寫能夠長時(shí)間運(yùn)行而不被用戶所中斷的后臺程序,去執(zhí)行事務(wù)或者邏輯,并同時(shí)保證頁面對用戶的及時(shí)響應(yīng)。本文深入 HTML5 多線程規(guī)范,講述多線程實(shí)現(xiàn)原理、方法,同時(shí)以實(shí)例的形式講解 HTML5 中多線程編程以及應(yīng)用。

W3C 中的工作線程規(guī)范到目前為止已經(jīng)定義了出了一系列公共接口,它允許 Web 程序開發(fā)人員去創(chuàng)建后臺線程在他們的主頁面中并發(fā)的運(yùn)行腳本。這將使得線程級別的消息通信成為現(xiàn)實(shí)。

詳解 HTML5 工作線程原理

傳統(tǒng)上的線程可以解釋為輕量級進(jìn)程,它和進(jìn)程一樣擁有獨(dú)立的執(zhí)行控制,一般情況下由操作系統(tǒng)負(fù)責(zé)調(diào)度。而在 HTML5 中的多線程是這樣一種機(jī)制,它允許在 Web 程序中并發(fā)執(zhí)行多個(gè) JavaScript 腳本,每個(gè)腳本執(zhí)行流都稱為一個(gè)線程,彼此間互相獨(dú)立,并且有瀏覽器中的 JavaScript 引擎負(fù)責(zé)管理。下面我們將詳細(xì)講解 HTML5 的工作線程原理。

工作線程與多線程編程

在 HTML5 中,工作線程的出現(xiàn)使得在 Web 頁面中進(jìn)行多線程編程成為可能。眾所周知,傳統(tǒng)頁面中(HTML5 之前)的 JavaScript 的運(yùn)行都是以單線程的方式工作的,雖然有多種方式實(shí)現(xiàn)了對多線程的模擬(例如:JavaScript 中的 setinterval 方法,setTimeout 方法等),但是在本質(zhì)上程序的運(yùn)行仍然是由 JavaScript 引擎以單線程調(diào)度的方式進(jìn)行的。在 HTML5 中引入的工作線程使得瀏覽器端的 JavaScript 引擎可以并發(fā)地執(zhí)行 JavaScript 代碼,從而實(shí)現(xiàn)了對瀏覽器端多線程編程的良好支持。

HTML5 中的 Web Worker 可以分為兩種不同線程類型,一個(gè)是專用線程 Dedicated Worker,一個(gè)是共享線程 Shared Worker。兩種類型的線程各有不同的用途。下面對這兩種工作線程作了詳細(xì)的說明和描述。

專用線程:Dedicated Worker

1. 專用線程(dedicated worker)的創(chuàng)建方式:

在創(chuàng)建專用線程的時(shí)候,需要給 Worker 的構(gòu)造函數(shù)提供一個(gè)指向 JavaScript 文件資源的 URL,這也是創(chuàng)建專用線程時(shí) Worker 構(gòu)造函數(shù)所需要的唯一參數(shù)。當(dāng)這個(gè)構(gòu)造函數(shù)被調(diào)用之后,一個(gè)工作線程的實(shí)例便會(huì)被創(chuàng)建出來。下面是創(chuàng)建專用線程代碼示例:

  • 清單 1. 創(chuàng)建專用線程示例代碼
  1. var worker = new Worker('dedicated.js');  

2. 與一個(gè)專用線程通信:

專用線程在運(yùn)行的過程中會(huì)在后臺使用 MessagePort 對象,而 MessagePort 對象支持 HTML5 中多線程提供的所有功能,例如:可以發(fā)送和接受結(jié)構(gòu)化數(shù)據(jù)(JSON 等),傳輸二進(jìn)制數(shù)據(jù),并且支持在不同端口中傳輸數(shù)據(jù)等。

為了在頁面主程序接收從專用線程傳遞過來的消息,我們需要使用工作線程的 onmessage 事件處理器,定義 onmessage 的實(shí)例代碼如下:

  •  清單 2. 接收來至工作線程示例代碼
  1. worker.onmessage = function (event) { ... };    

3.另外,開發(fā)人員也可以選擇使用 addEventListener 方法,它最終的實(shí)現(xiàn)方式和作用和 onmessage 相同。

   就像前面講述的,專用線程會(huì)使用隱式的 MessagePort 實(shí)例,當(dāng)專用線程被創(chuàng)建的時(shí)候,MessagePort 的端口消息隊(duì)列便被主動(dòng)啟用。因此,這也和工作線程接口中定義的 start 方法作用一   致。

    如果要想一個(gè)專用線程發(fā)送數(shù)據(jù),那么我們需要使用線程中的 postMessage 方法。專用線程不僅僅支持傳輸二進(jìn)制數(shù)據(jù),也支持結(jié)構(gòu)化的 JavaScript 數(shù)據(jù)格式。在這里有一點(diǎn)需要注意,為了高效地傳輸 ArrayBuffer 對象數(shù)據(jù),需要在 postMessage 方法中的第二個(gè)參數(shù)中指定它。實(shí)例代碼如下:

  • 清單 3. 高效的發(fā)送 ArrayBuffer 數(shù)據(jù)代碼
  1. worker.postMessage({ operation: 'list_all_users',   
  2. //ArrayBuffer object     
  3. input: buffer,     threshold: 0.8,    }, [buffer]);    

共享線程 Shared Worker

1.共享線程

共享線程可以由兩種方式來定義:一是通過指向 JavaScript 腳本資源的 URL 來創(chuàng)建,而是通過顯式的名稱。當(dāng)由顯式的名稱來定義的時(shí)候,由創(chuàng)建這個(gè)共享線程的第一個(gè)頁面中使用 URL 會(huì)被用來作為這個(gè)共享線程的 JavaScript 腳本資源 URL。通過這樣一種方式,它允許同域中的多個(gè)應(yīng)用程序使用同一個(gè)提供公共服務(wù)的共享線程,從而不需要所有的應(yīng)用程序都去與這個(gè)提供公共服務(wù)的 URL 保持聯(lián)系。

無論在什么情況下,共享線程的作用域或者是生效范圍都是由創(chuàng)建它的域來定義的。因此,兩個(gè)不同的站點(diǎn)(即域)使用相同的共享線程名稱也不會(huì)沖突。

 

2.共享線程的創(chuàng)建

創(chuàng)建共享線程可以通過使用 SharedWorker() 構(gòu)造函數(shù)來實(shí)現(xiàn),這個(gè)構(gòu)造函數(shù)使用 URL 作為第一個(gè)參數(shù),即是指向 JavaScript 資源文件的 URL,同時(shí),如果開發(fā)人員提供了第二個(gè)構(gòu)造參數(shù),那么這個(gè)參數(shù)將被用于作為這個(gè)共享線程的名稱。創(chuàng)建共享線程的代碼示例如下:

  1. / 從端口接收數(shù)據(jù) , 包括文本數(shù)據(jù)以及結(jié)構(gòu)化數(shù)據(jù) 1. worker.port.onmessage = function (event) { define your logic here... };  // 向端口發(fā)送普通文本數(shù)據(jù)  
  2.  
  3. worker.port.postMessage('put your message here … ');  // 向端口發(fā)送結(jié)構(gòu)化數(shù)據(jù)  
  4.  worker.port.postMessage({ username: 'usertext'; live_city:  ['data-one', 'data-two', 'data-three','data-four']});

 

3.與共享線程通信

共享線程的通信也是跟專用線程一樣,是通過使用隱式的 MessagePort 對象實(shí)例來完成的。當(dāng)使用 SharedWorker() 構(gòu)造函數(shù)的時(shí)候,這個(gè)對象將通過一種引用的方式被返回回來。我們可以通過這個(gè)引用的 port 端口屬性來與它進(jìn)行通信。發(fā)送消息與接收消息的代碼示例如下:

  • 清單 4. 發(fā)送消息與接收消息代碼

上面示例代碼中,第一個(gè)我們使用 onmessage 事件處理器來接收消息,第二個(gè)使用 postMessage 來發(fā)送普通文本數(shù)據(jù),第三個(gè)使用 postMessage 來發(fā)送結(jié)構(gòu)化的數(shù)據(jù),這里我們使用了 JSON 數(shù)據(jù)格式。

工作線程事件處理模型

當(dāng)工作線程被一個(gè)具有 URL 參數(shù)的構(gòu)造函數(shù)創(chuàng)建的時(shí)候,它需要有一系列的處理流程來處理和記錄它本身的數(shù)據(jù)和狀態(tài)。下面我們給出了工作線程的處理模型如下(注:由于 W3C 中工作線程的規(guī)范依然在更新,您讀到這篇文章的時(shí)候可能看到已不是最新的處理模型,建議參考 W3C 中的最新規(guī)范):

1. 創(chuàng)建一個(gè)獨(dú)立的并行處理環(huán)境,并且在這個(gè)環(huán)境里面異步的運(yùn)行下面的步驟。

2. 如果它的全局作用域是 SharedWorkerGlobalScope 對象,那么把最合適的應(yīng)用程序緩存和它聯(lián)系在一起。

3. 嘗試從它提供的 URL 里面使用 synchronous 標(biāo)志和 force same-origin 標(biāo)志獲取腳本資源。

4. 新腳本創(chuàng)建的時(shí)候會(huì)按照下面的步驟:

  1. 創(chuàng)建這個(gè)腳本的執(zhí)行環(huán)境。
  2. 使用腳本的執(zhí)行環(huán)境解析腳本資源。
  3. 設(shè)置腳本的全局變量為工作線程全局變量。
  4. 設(shè)置腳本編碼為 UTF-8 編碼。

5. 啟動(dòng)線程監(jiān)視器,關(guān)閉孤兒線程。

6. 對于掛起線程,啟動(dòng)線程監(jiān)視器監(jiān)視掛起線程的狀態(tài),即時(shí)在并行環(huán)境中更改它們的狀態(tài)。

7. 跳入腳本初始點(diǎn),并且啟動(dòng)運(yùn)行。

8. 如果其全局變量為 DedicatedWorkerGlobalScope 對象,然后在線程的隱式端口中啟用端口消息隊(duì)列。

9. 對于事件循環(huán),等待一直到事件循環(huán)列表中出現(xiàn)新的任務(wù)。

10. 首先運(yùn)行事件循環(huán)列表中的最先進(jìn)入的任務(wù),但是用戶代理可以選擇運(yùn)行任何一個(gè)任務(wù)。

11. 如果事件循環(huán)列表擁有存儲(chǔ) mutex 互斥信號量,那么釋放它。

12. 當(dāng)運(yùn)行完一個(gè)任務(wù)后,從事件循環(huán)列表中刪除它。

13. 如果事件循環(huán)列表中還有任務(wù),那么繼續(xù)前面的步驟執(zhí)行這些任務(wù)。

14. 如果活動(dòng)超時(shí)后,清空工作線程的全局作用域列表。

15. 釋放工作線程的端口列表中的所有端口。

#p#

工作線程應(yīng)用范圍和作用域

工作線程的全局作用域僅僅限于工作線程本身,即在線程的生命周期內(nèi)有效。規(guī)范中 WorkerGlobalScope 接口代表了它的全局作用域,下面我們來看下這個(gè)接口的具體實(shí)施細(xì)節(jié)(WorkerGlobalScope 抽象接口)。

  • 清單 5. WorkerGlobalScope 抽象接口代碼
  1. interface WorkerGlobalScope {   
  2. readonly attribute WorkerGlobalScope self;   
  3. readonly attribute WorkerLocation location;    
  4. void close();            attribute Function onerror;  
  5. };  
  6. WorkerGlobalScope implements WorkerUtils;  
  7. WorkerGlobalScope implements EventTarget;   

我們可以使用 WorkerGlobalScope 的 self 屬性來或者這個(gè)對象本身的引用。location 屬性返回當(dāng)線程被創(chuàng)建出來的時(shí)候與之關(guān)聯(lián)的 WorkerLocation 對象,它表示用于初始化這個(gè)工作線程的腳步資源的絕對 URL,即使頁面被多次重定向后,這個(gè) URL 資源位置也不會(huì)改變。

當(dāng)腳本調(diào)用 WorkerGlobalScope 上的 close()方法后,會(huì)自動(dòng)的執(zhí)行下面的兩個(gè)步驟:

1. 刪除這個(gè)工作線程事件隊(duì)列中的所有任務(wù)。

2. 設(shè)置 WorkerGlobalScope 對象的 closing 狀態(tài)為 true (這將阻止以后任何新的任務(wù)繼續(xù)添加到事件隊(duì)列中來)。

工作線程生命周期

工作線程之間的通信必須依賴于瀏覽器的上下文環(huán)境,并且通過它們的 MessagePort 對象實(shí)例傳遞消息。每個(gè)工作線程的全局作用域都擁有這些線程的端口列表,這些列表包括了所有線程使用到的 MessagePort 對象。在專用線程的情況下,這個(gè)列表還會(huì)包含隱式的 MessagePort 對象。

每個(gè)工作線程的全局作用域?qū)ο?WorkerGlobalScope 還會(huì)有一個(gè)工作線程的線程列表,在初始化時(shí)這個(gè)列表為空。當(dāng)工作線程被創(chuàng)建的時(shí)候或者擁有父工作線程的時(shí)候,它們就會(huì)被填充進(jìn)來。

最后,每個(gè)工作線程的全局作用域?qū)ο?WorkerGlobalScope 還擁有這個(gè)線程的文檔模型,在初始化時(shí)這個(gè)列表為空。當(dāng)工作線程被創(chuàng)建的時(shí)候,文檔對象就會(huì)被填充進(jìn)來。無論何時(shí)當(dāng)一個(gè)文檔對象被丟棄的時(shí)候,它就要從這個(gè)文檔對象列舉里面刪除出來。

在工作線程的生命周期中,定義了下面四種不同類型的線程名稱,用以標(biāo)識它們在線程的整個(gè)生命周期中的不同狀態(tài):

  • 當(dāng)一個(gè)工作線程的文檔對象列舉不為空的時(shí)候,這個(gè)工作線程會(huì)被稱之為許可線程。(A worker is said to be a permissible worker if its list of the worker's Documents is not empty.)
  • 當(dāng)一個(gè)工作線程是許可線程并且或者擁有數(shù)據(jù)庫事務(wù)或者擁有網(wǎng)絡(luò)連接或者它的工作線程列表不為空的時(shí)候,這個(gè)工作線程會(huì)被稱之為受保護(hù)的線程。(A worker is said to be a protected worker if it is a permissible worker and either it has outstanding timers, database transactions, or network connections, or its list of the worker's ports is not empty)
  • 當(dāng)一個(gè)工作線程的文檔對象列表中的任何一個(gè)對象都是處于完全活動(dòng)狀態(tài)的時(shí)候,這個(gè)工作線程會(huì)被稱之為需要激活線程。(A worker is said to be an active needed worker if any of the Document objects in the worker's Documents are fully active.)
  • 當(dāng)一個(gè)工作線程是一個(gè)非需要激活線程同時(shí)又是一個(gè)許可線程的時(shí)候,這個(gè)工作線程會(huì)被稱之為掛起線程。(A worker is said to be a suspendable worker if it is not an active needed worker but it is a permissible worker.)

由于 W3C 的 Web Worker 規(guī)范目前還是處于完善階段,沒有形成最終的規(guī)范,本文也將上面線程的四種不同狀態(tài)的原文定義附在了后面。

工作線程(Web Worker)API 接口

類庫和腳本的訪問和引入

對于類庫和腳本的訪問和引入,規(guī)范中規(guī)定可以使用 WorkerGlobalScope 對象的 importScripts(urls) 方法來引入網(wǎng)絡(luò)中的腳本資源。當(dāng)用戶調(diào)用這個(gè)方法引入資源的時(shí)候會(huì)執(zhí)行下面的步驟來完成這個(gè)操作:

  1. 如果沒有給 importScripts 方法任何參數(shù),那么立即返回,終止下面的步驟。
  2. 解析 importScripts 方法的每一個(gè)參數(shù)。
  3. 如果有任何失敗或者錯(cuò)誤,拋出 SYNTAX_ERR 異常。
  4. 嘗試從用戶提供的 URL 資源位置處獲取腳本資源。
  5. 對于 importScripts 方法的每一個(gè)參數(shù),按照用戶的提供順序,獲取腳本資源后繼續(xù)進(jìn)行其它操作。
  • 清單 6. 外部資源腳本引入和訪問示例代碼
  1. /**  
  2.  * 使用 importScripts 方法引入外部資源腳本,在這里我們使用了數(shù)學(xué)公式計(jì)算工具庫 math_utilities.js  
  3.  * 當(dāng) JavaScript 引擎對這個(gè)資源文件加載完畢后,繼續(xù)執(zhí)行下面的代碼。同時(shí),下面的的代碼可以訪問和調(diào)用 
  4.  * 在資源文件中定義的變量和方法。 
  5.  **/  
  6.  
  7. importScripts('math_utilities.js');   
  8. /**  * This worker is used to calculate  * the least common multiple  * and the greatest common divisor  */  
  9. onmessage = function (event)  {  
  10. var first=event.data.first;  var second=event.data.second;  calculate(first,second);  };    
  11. /*  * calculate the least common multiple  * and the greatest common divisor  */  
  12. function calculate(first,second) {     
  13. //do the calculation work  var common_divisor=divisor(first,second);  
  14. var common_multiple=multiple(first,second);     
  15. postMessage("Work done! " +  The least common multiple is "+common_divisor  +" and the greatest common divisor is "+common_multiple);  }   

工作導(dǎo)航器對象(WorkerNavigator)

在 HTML5 中, WorkerUtils 接口的 navigator 屬性會(huì)返回一個(gè)工作導(dǎo)航器對象(WorkerNavigator),這個(gè)對象定義并且代表了用戶代理(即 Web 客戶端)的標(biāo)識和狀態(tài)。因此,用戶和 Web 腳本開發(fā)人員可以在多線程開發(fā)過程中通過這個(gè)對象來取得或者確定用戶的狀態(tài)。

  1. 工作導(dǎo)航器對象(WorkerNavigator)

    WorkerUtils 抽象接口的 navigator 屬性會(huì)返回一個(gè) WorkerNavigator 用戶接口,用于用戶代理的識別的狀態(tài)標(biāo)識。我們來看下 WorkerNavigator 接口的定義。

  2. WorkerNavigator 接口定義

    清單 7. WorkerNavigator 接口定義代碼
    1. interface WorkerNavigator {};  
    2.  WorkerNavigator implements NavigatorID;  
    3.  WorkerNavigator implements NavigatorOnLine; 

其中,有一點(diǎn)需要注意:如果接口的相對命名空間對象為 Window 對象的時(shí)候,WorkerNavigator 對象一定不可以存在,即無法再使用這個(gè)對象。

創(chuàng)建與終止線程

在講解創(chuàng)建新的工作線程之前,我們先看下 W3C 規(guī)范對工作線程的定義。工作線程規(guī)范中定義了線程的抽象接口類 AbstractWorker ,專用線程以及共享線程都繼承自該抽象接口。專用線程以及共享線程的創(chuàng)建方法讀者可以參考第一小節(jié)中的示例代碼。下面是此抽象接口的定義。

   1.AbstractWorker 抽象接口

  • 清單 8. AbstractWorker 抽象接口代碼

       此外,該接口還定義了錯(cuò)誤處理的事件處理器 onerror,當(dāng)工作線程在通信過程中遇到錯(cuò)誤時(shí)便會(huì)觸發(fā)這個(gè)事件處理器。

   2.專用線程及其定義

  • 清單 9. 專用線程定義代碼
    1. [Constructor(in DOMString scriptURL)]  
    2.  interface Worker : AbstractWorker {  
    3.   void terminate();  
    4.  
    5.   void postMessage(in any message, in optional MessagePortArray ports);  
    6.            attribute Function onmessage;  
    7.  };  

        當(dāng)創(chuàng)建完線程以后,我們可以調(diào)用 terminate() 方法去終止一個(gè)線程。每個(gè)專用線程都擁有一個(gè)隱式的 MessagePort 對象與之相關(guān)聯(lián)。這個(gè)端口隨著線程的創(chuàng)建而被創(chuàng)建出來,但并沒有暴露給用戶。所有的基于這個(gè)端口的消息接收都以線程本身為目標(biāo)。

    3.共享線程及其定義

  • 清單 10. 共享線程定義代碼   
    1. [Constructor(DOMString scriptURL, optional DOMString name)]  
    2. interface SharedWorker : AbstractWorker {  
    3. readonly attribute MessagePort port;  
    4. };  

共享線程同專用線程一樣,當(dāng)創(chuàng)建完線程以后,我們可以調(diào)用 terminate() 方法去終止一個(gè)共享線程。

#p#

工作線程位置屬性

工作線程被創(chuàng)建出來以后,需要記錄它的狀態(tài)以及位置信息,在工作線程規(guī)范中定義了 WorkerLocation 來表示它們的位置。接口定義如下:

  • 清單 11. 共享線程定義代碼
    1. interface WorkerLocation {  
    2.   // URL decomposition IDL attributes  
    3.   stringifier readonly attribute DOMString href;  
    4.   readonly attribute DOMString protocol;  
    5.   readonly attribute DOMString host;  
    6.   readonly attribute DOMString hostname;  
    7.   readonly attribute DOMString port;  
    8.   readonly attribute DOMString pathname;  
    9.   readonly attribute DOMString search;  
    10.   readonly attribute DOMString hash;  
    11.  };  

WorkerLocation 對象表示了工作線程腳本資源的絕對 URL 信息。我們可以使用它的 href 屬性取得這個(gè)對象的絕對 URL。WorkerLocation 接口還定義了與位置信息有關(guān)的其它屬性,例如:用于信息傳輸?shù)膮f(xié)議(protocol),主機(jī)名稱(hostname),端口(port),路徑名稱(pathname)等。

工作線程(Web Worker)應(yīng)用與實(shí)踐

我們可以寫出很多的例子來說明后臺工作線程的合適的用法,下面我們以幾種典型的應(yīng)用場景為例,用代碼實(shí)例的形式講解在各種需求背景下正確的使用它們。

應(yīng)用場景一:使用工作線程做后臺數(shù)值(算法)計(jì)算

工作線程最簡單的應(yīng)用就是用來做后臺計(jì)算,而這種計(jì)算并不會(huì)中斷前臺用戶的操作。下面我們提供了一個(gè)工作線程的代碼片段,用來執(zhí)行一個(gè)相對來說比較復(fù)雜的任務(wù):計(jì)算兩個(gè)非常大的數(shù)字的最小公倍數(shù)和最大公約數(shù)。

在這個(gè)例子中,我們在主頁面中創(chuàng)建一個(gè)后臺工作線程,并且向這個(gè)工作線程分配任務(wù)(即傳遞兩個(gè)特別大的數(shù)字),當(dāng)工作線程執(zhí)行完這個(gè)任務(wù)時(shí),便向主頁面程序返回計(jì)算結(jié)果,而在這個(gè)過程中,主頁面不需要等待這個(gè)耗時(shí)的操作,可以繼續(xù)進(jìn)行其它的行為或任務(wù)。

我們把這個(gè)應(yīng)用場景分為兩個(gè)主要部分,一個(gè)是主頁面,可以包含主 JavaScript 應(yīng)用入口,用戶其它操作 UI 等。另外一個(gè)是后臺工作線程腳本,即用來執(zhí)行計(jì)算任務(wù)。代碼片段如下:

  • 清單 12. 主程序頁面代碼
  1. <!DOCTYPE HTML>  
  2.  <html>  
  3.  <head>  
  4.  <title>  
  5.  Background Worker Application Example 1: Complicated Number Computation  
  6.  </title>  
  7.  </head>  
  8.  <body>  
  9.  <div>  
  10.  The least common multiple and greatest common divisor is:  
  11.  <p id="computation_results">please wait, computing … </p>  
  12.  </div>  
  13.   <script>  
  14.    var worker = new Worker('numberworker.js');  
  15.  worker.postMessage("{first:347734080,second:3423744400}");  
  16.    worker.onmessage = function (event)  
  17.  {  
  18.   document.getElementById(' computation_result').textContent = event.data;  
  19.  };  
  20.   </script>  
  21.  </body>  
  22.  </html>  
  • 清單 13. 后臺工作線程代碼
    1. /**   
    2.  * This worker is used to calculate   
    3.  * the least common multiple   
    4.  * and the greatest common divisor   
    5.  */   
    6.   
    7. onmessage = function (event)   {   
    8.  var first=event.data.first;    
    9. var second=event.data.second;    
    10. calculate(first,second);   };      
    11. /*   * calculate the least common multiple   * and the greatest common divisor   */    
    12. function calculate(first,second) {       
    13. //do the calculation work    
    14. var common_divisor=divisor(first,second);    
    15. var common_multiple=multiple(first,second);      
    16.  postMessage("Work done! " +  "The least common multiple is "+common_divisor   +" and the greatest common divisor is "+common_multiple);   }      
    17.  /**   * calculate the greatest common divisor   * @param number   * @param number   * @return   */    
    18. function divisor(a, b) {    
    19. if (a % b == 0) {   return b;   }  
    20. else {   return divisor(b, a % b);   }   }     
    21. /**   * calculate the least common multiple   * @param number   * @param number   * @return   */    
    22. function multiple( a,  b) {   var multiple = 0;   multiple = a * b / divisor(a, b);   return multiple;   }    

在主程序頁面中,我們使用 Worker()構(gòu)造函數(shù)創(chuàng)建一個(gè)新的工作線程,它會(huì)返回一個(gè)代表此線程本身的線程對象。接下來我們使用這個(gè)線程對象與后臺腳本進(jìn)行通信。線程對象有兩個(gè)主要事件處理器:postMessage 和 onmessage 。postMessage 用來向后臺腳本發(fā)送消息,onmessage 用以接收從后臺腳本中傳遞過來的消息。

在后臺工作線程代碼片段中,我們定一個(gè)兩個(gè) JavaScript 函數(shù),一個(gè)是 function divisor:用以計(jì)算最大公約數(shù),一個(gè)是 function multiple:用以計(jì)算最小公倍數(shù)。同時(shí)工作線程的 onmessage 事件處理器用以接收從主頁面中傳遞過來的數(shù)值,然后把這兩個(gè)數(shù)值傳遞到 function calculate 用以計(jì)算。當(dāng)計(jì)算完成后,調(diào)用事件處理器 postMessage,把計(jì)算結(jié)果發(fā)送到主頁面。

應(yīng)用場景二:使用共享線程處理多用戶并發(fā)連接

由于線程的構(gòu)建以及銷毀都要消耗很多的系統(tǒng)性能,例如 CPU 的處理器調(diào)度,內(nèi)存的占用回收等,在一般的編程語言中都會(huì)有線程池的概念,線程池是一種對多線程并發(fā)處理的形式,在處理過程中系統(tǒng)將所有的任務(wù)添加到一個(gè)任務(wù)隊(duì)列,然后在構(gòu)建好線程池以后自動(dòng)啟動(dòng)這些任務(wù)。處理完任務(wù)后再把線程收回到線程池中,用于下一次任務(wù)調(diào)用。線程池也是共享線程的一種應(yīng)用。

在 HTML5 中也引入了共享線程技術(shù),但是由于每個(gè)共享線程可以有多個(gè)連接,HTML5 對共享線程提供了和普通工作線程稍微有些區(qū)別的 API 接口。下面我們提供幾個(gè)例子來講述對共享線程的用法。

下面我們給出一個(gè)例子:創(chuàng)建一個(gè)共享線程用于接收從不同連接發(fā)送過來的指令,然后實(shí)現(xiàn)自己的指令處理邏輯,指令處理完成后將結(jié)果返回到各個(gè)不同的連接用戶。

 

  • 清單 14. 共享線程用戶連接頁面代碼
    1. <!DOCTYPE html>  
    2. <html>   
    3. <head>   <meta charset="UTF-8">   
    4. <title>Shared worker example: how to use shared worker in HTML5</title>    
    5. <script>    var worker = new SharedWorker('sharedworker.js');    
    6. var log = document.getElementById('response_from_worker');   
    7. worker.port.addEventListener('message', function(e) {   
    8. //log the response data in web page   log.textContent =e.data;    }, false);   
    9.  worker.port.start();    worker.port.postMessage('ping from user web page..');      
    10.  //following method will send user input to sharedworker    
    11. function postMessageToSharedWorker(input)    {    
    12. //define a json object to construct the request    
    13. var instructions={instruction:input.value};   
    14.  worker.port.postMessage(instructions);    }  </script>   
    15. </head>  
    16. <body onload=''>   
    17. <output id='response_from_worker'>  
    18.  Shared worker example: how to use shared worker in HTML5   </output>  
    19.  send instructions to shared worker:  
    20. <input type="text" autofocus oninput="postMessageToSharedWorker(this);return false;">   
    21. </input>  
    22. </body>  
    23. </html>  
  • 清單 15. 用于處理用戶指令的共享線程代碼
  1. // 創(chuàng)建一個(gè)共享線程用于接收從不同連接發(fā)送過來的指令,指令處理完成后將結(jié)果返回到各個(gè)不同的連接用戶。 
  2.  
  3.  /*  
  4.  * define a connect count to trace connecting  
  5.  * this variable will be shared within all connections  
  6.  */  
  7.  
  8. var connect_number = 0;   onconnect = function(e) {   
  9. connect_numberconnect_numberconnect_number =connect_number+ 1;   
  10. //get the first port here   var port = e.ports[0];   
  11. port.postMessage('A new connection! The current connection number is '   + connect_number);   port.onmessage = function(e) { 
  12.  //get instructions from requester  
  13. var instruction=e.data.instruction;  
  14. var results=execute_instruction(instruction);   
  15.   port.postMessage('Request: '+instruction+' Response '+results     +' from shared worker...');  
  16.  };  
  17. };  
  18.  /*  * this function will be used to execute the instructions send from requester  * @param instruction  * @return  */  
  19. function execute_instruction(instruction)  {  
  20. var result_value;  
  21. //implement your logic here  //execute the instruction...  
  22. return result_value  }   

#p#

  • 清單 16. 主頁面(僅僅是用來顯示計(jì)算結(jié)果)代碼
  1. <!DOCTYPE html>  
  2. <html>  
  3. <head>   
  4. <meta charset="UTF-8">   
  5. <title>Shared worker example: how to use delegation worker in HTML5</title>   
  6. <script>   
  7.  var worker = new SharedWorker('delegationworker.js');   
  8.  var log = document.getElementById('response_from_worker');   
  9.  worker.onmessage = function (event) {    
  10. //resolve the population from delegation worker    
  11. var resultdata=event.data;    
  12. var population=resultdata.total_population;    
  13. var showtext='The total population of the word is '+population;    
  14. document.getElementById('response_from_worker').textContent = showtext;    };  
  15.  </script>    
  16. </head>   
  17. <body onload=''>   
  18. <output id='response_from_worker'>   Shared worker example: how to use delegation worker in HTML5   </output>   
  19. </body>   
  20. </html>  
  • 清單 17. 主工作線程代碼
    1. /*  
    2.  * define the country list in the whole word  
    3.  * take following Array as an example  
    4.  */  
    5.  
    6. var country_list = ['Albania','Algeria','American','Andorra','Angola','Antigua','....'];   
    7.  // define the variable to record the population of the word   var total_population=0;  
    8.  var country_size=country_list.length;   var processing_size=country_list.length;    
    9. for (var i = 0; i < country_size; i++)   {   
    10. var worker = new Worker('subworker.js');    
    11. //wrap the command, send to delegations   
    12.  var command={command:'start',country:country_list[i]};    
    13. worker.postMessage(command);    worker.onmessage = update_results;   }    
    14. /*   * this function will be used to update the result   * @param event   * @return   */    
    15. function storeResult(event)   {   
    16. total_population += event.data;  
    17.  processing_size -1;   
    18. if (processing_size <= 0)   
    19. {   //complete the whole work, post results to web page  
    20.  postMessage(total_population);   }   }  
  • 清單 18. 代理線程代碼
  1. //define the onmessage hander for the delegation  
  2.  
  3.  
  4. onmessage = start_calculate;   
  5.  /*  
  6.  * resolve the command and kick off the calculation  
  7.  */  
  8.   
  9.  
  10. function start_calculate(event)   {   
  11. var command=event.data.command;   
  12. if(command!=null&&command=='start')   {   
  13. var coutry=event.data.country;  
  14. do_calculate(country);   }   
  15. onmessage = null;   }   
  16.  /*  
  17.  * the complex calculation method defined here  
  18.  * return the population of the country  
  19.  */  
  20.   
  21. function do_calculate(country)   {    
  22. var population = 0;    
  23. var cities=//get all the cities for this country    
  24. for (var i = 0; i < cities.length; i++){    
  25. var city_popu=0;        
  26. // perform the calculation for this city    
  27. //update the city_popu    
  28. population += city_popu;    }   
  29.   postMessage(population);    
  30. close();   }   

總結(jié)

HTML5 Web Worker 的多線程特性為基于 Web 系統(tǒng)開發(fā)的程序人員提供了強(qiáng)大的并發(fā)程序設(shè)計(jì)功能,它允許開發(fā)人員設(shè)計(jì)開發(fā)出性能和交互更好的富客戶端應(yīng)用程序。本文不僅僅詳細(xì)講述 HTML5 中的多線程規(guī)范。同時(shí),也以幾種典型的應(yīng)用場景為例,以實(shí)例的形式講解 HTML5 中多線程編程以及應(yīng)用,為用戶提供了詳細(xì)而全面的參考價(jià)值,并且指導(dǎo)開發(fā)人員設(shè)計(jì)和構(gòu)建更為高效和穩(wěn)定的 Web 多線程應(yīng)用。

原文鏈接:http://www.ibm.com/developerworks/cn/web/1112_sunch_webworker/index.html

 

責(zé)任編輯:陳四芳 來源: developerWorks中國
相關(guān)推薦

2011-02-23 14:57:41

webweb開發(fā)HTML

2024-05-17 12:56:09

C#編程線程

2009-02-24 08:36:51

多線程線程池網(wǎng)絡(luò)服務(wù)器

2012-02-23 10:28:43

AppCanHTML5移動(dòng)應(yīng)用

2023-06-16 08:36:25

多線程編程數(shù)據(jù)競爭

2024-01-09 08:28:44

應(yīng)用多線程技術(shù)

2013-03-22 08:59:57

HTML5移動(dòng)應(yīng)用Web App

2014-03-20 10:50:44

HTML5 定位技術(shù)

2011-05-11 12:59:18

HTML5

2013-07-16 10:12:14

iOS多線程多線程概念多線程入門

2012-08-30 16:24:04

HTML5歐朋W3C

2012-06-27 17:17:55

HTML5

2012-06-20 15:21:11

HTML5Opera歐朋瀏覽器

2023-06-13 13:39:00

多線程異步編程

2013-08-08 10:00:01

Amazon AppsHTML5

2011-11-28 13:15:25

HTML5移動(dòng)應(yīng)用

2012-03-29 09:18:47

HTML5WEB

2015-06-10 10:18:27

WebAPP開發(fā)技巧

2009-03-12 10:52:43

Java線程多線程

2013-01-24 10:26:04

HTML5HTML 5HTML5的未來
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號