使用ES6玩轉(zhuǎn)多線程

一、需求

JS著名的Event Loop限制了使用多線程的想象力北秽,這對于高并發(fā)IO操作是不錯的選擇,但對于高并發(fā)的CPU型運算狼牺,必然是捉襟見肘羡儿。


Event Loop

二、早期的NodeJs解決之道

依賴于強(qiáng)大的V8引擎是钥,nodeJS可以借助于對系統(tǒng)底層的調(diào)用掠归,利用子進(jìn)程完成對并發(fā)計算的需求,體現(xiàn)在child_process和cluster這兩個模塊悄泥。
但進(jìn)程的內(nèi)存區(qū)不可共享虏冻,進(jìn)程切換開銷,及通過Binding Bridge的通信弹囚,將極大的限制NodeJS的工作效率厨相!

三、ES6 的MessageChannel 是解決問題的突破口

MessageChannel和MessagePort
  1. 演示代碼(瀏覽器環(huán)境):
var channel=new MessageChannel();
let book={id:1}
//默認(rèn):book將對序列化的方式進(jìn)行發(fā)送
//port1發(fā)送時,由port2時行接收
channel.port1.postMessage(book);

//默認(rèn):event.data,是book對象的"深克隆對象"
channel.port2.onmessage=function (event) {
    console.log(event.data.id) //show 1
}
  1. 分析:

此時蛮穿,所有的處理工作都是在event loop的線程中執(zhí)行庶骄,如果channel的兩頭分別是兩個不同的線程,就可以完成多線程的通信重任了践磅!

  1. 未完成的筆記:
    后續(xù)會用一篇文章分析三種不同形式的postMessage參數(shù)情況单刁。

四、先談下Web Workers

由ES6引入的子線程的概念府适,可以使用new Worker(url)羔飞,將url指向的js代碼,放入子線程中執(zhí)行檐春,詳見代碼(以瀏覽器環(huán)境為例):

  1. 在html文件中
    <script>
        //啟動Worker子線程逻淌,并傳入?yún)?shù)
        var worker=new Worker('sub.js',{name:'john'})
        //進(jìn)行異常處理
        worker.onerror=function (err) {
            console.log(err.message)
        }
    </script>
  1. 被子線程加載的代碼 sub.js
//這里的代碼將在子線程中執(zhí)行
//注意:這里不是能訪問document,window對象的,但可以訪問location,navigator對象
var name=self.name //獲取啟動時傳入的信息
throw new Error('name is recieved!')  //將在主線程中的worker.onerror中接收到
self.close() //強(qiáng)行關(guān)閉子線程(并不需要顯示調(diào)用)
  1. 運行結(jié)果
Uncaught Error: john is recieved!

五疟暖、父子線程通信

  1. 原理:利用一個匿名管理(MessageChannel),利用其port1,port2完成通訊
  2. 代碼(以瀏覽器環(huán)境為例)
    html中
   <script>
        //啟動Worker子線程卡儒,并傳入?yún)?shù)
        var worker=new Worker('sub.js',{name:'john'})

        //父線程向子線程發(fā)送
        worker.postMessage({name:"good js"})
        //等待子線程發(fā)送過來的數(shù)據(jù)
        worker.onmessage=function (event) {
            console.log(event.data)
        }
    </script>

sub.js(子線程)

//這里的代碼將在子線程中執(zhí)行
var name=self.name //獲取啟動時傳入的信息
self.onmessage=function(event){
    //這里獲取的是一個深克隆對象
    var book=event.data;
    book.id=1
    //子線程向父線程發(fā)送數(shù)據(jù)
    self.postMessage(book)
}
  1. 運行結(jié)果
{name: "good js", id: 1}

六、postMessage的參數(shù):

  1. 默認(rèn)情況下誓篱,會對參數(shù)時行序列化操作(要在接收方產(chǎn)生一個深克隆對象)朋贬,但其能力要遠(yuǎn)強(qiáng)于JSON.stringify,表現(xiàn)在以下:
    (1)可以處理循環(huán)引用問題
    (2)處理一些js內(nèi)置對象窜骄,如Set,Regexp等對象
    (3)能處理ArrayBuffer
    (4)能處理一些C++原生的對象锦募,如:MessagePort
    但要注意:參數(shù)對象,如果帶有方法時邻遏,處理時會拋出異常糠亩。
  2. 當(dāng)參數(shù)的類型為SharedArrayBuffer時,會將對象本身進(jìn)行共享(共享內(nèi)存區(qū))
const sharedUint8Array = new Uint8Array(new SharedArrayBuffer(4));
worker.postMessage(sharedUint8Array);
  1. 當(dāng)postMessage加上可選的第二個參數(shù)(TransferList)時准验,也表示“共享內(nèi)存區(qū)”赎线,但在TransferList加以標(biāo)明的對象,在發(fā)送完畢后糊饱,是不可以被使用的垂寥!
const uint8Array = new Uint8Array([ 1, 2, 3, 4 ]);
worker.postMessage(uint8Array, [ uint8Array.buffer ]);

此時uint8Array會以內(nèi)存區(qū)的形式直接共享,但由于其在第二個參數(shù)中加以注明(成為TranserList的一部分)另锋,所以其在主線程中滞项,是不可以再繼續(xù)使用的。

  1. TransferList的中的對象類型:
    只能是ArrayBuffer和MessagePort兩種類型夭坪。

七文判、利用MessageChannel和MessagePort完成子線程間的通信。

  1. 思路:在主線程中室梅,將MessageChannel的port1和port2戏仓,分別以postMessage的參數(shù)發(fā)送到兩個子線程疚宇,然后子線程利用收到的port,對channel進(jìn)行發(fā)送和接收赏殃,從而完成子線程間的通信敷待。
  2. 代碼:
    html:
    <script>
        //子線程通信用的channel
        var channel=new MessageChannel();
        //分別啟動兩個子線程
        var worker1=new Worker('sub1.js')
        var worker2=new Worker('sub2.js')
        //將port1和port2分別傳遞給子線程
        worker1.postMessage({port:channel.port1},[channel.port1])
        worker2.postMessage({port:channel.port2},[channel.port2])
    </script>

sub1.js和sub2.js

//接收從父線程發(fā)送過來的port
self.onmessage=function (event) {
    //利用port1進(jìn)行發(fā)送到channel(此時port2上出現(xiàn)數(shù)據(jù))
    event.data.port.postMessage("I am sub1 worker!")
  //利用port1監(jiān)聽channel(等待從port2發(fā)送過來的數(shù)據(jù))
    event.data.port.onmessage=function (event) {
        console.log("in sub1:"+event.data)
    }
}
self.onmessage=function (event) {
    event.data.port.postMessage("I am sub2 worker!")
    event.data.port.onmessage=function (event) {
        console.log("in sub2:"+event.data)
    }
}
  1. 運行結(jié)果:
in sub2:I am sub1 worker!
in sub1:I am sub2 worker!
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市嗓奢,隨后出現(xiàn)的幾起案子讼撒,更是在濱河造成了極大的恐慌浑厚,老刑警劉巖股耽,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異钳幅,居然都是意外死亡物蝙,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進(jìn)店門敢艰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來诬乞,“玉大人,你說我怎么就攤上這事钠导≌鸺担” “怎么了?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵牡属,是天一觀的道長票堵。 經(jīng)常有香客問我,道長逮栅,這世上最難降的妖魔是什么悴势? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮措伐,結(jié)果婚禮上特纤,老公的妹妹穿的比我還像新娘。我一直安慰自己侥加,他們只是感情好捧存,可當(dāng)我...
    茶點故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著担败,像睡著了一般昔穴。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上氢架,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天傻咖,我揣著相機(jī)與錄音,去河邊找鬼岖研。 笑死卿操,一個胖子當(dāng)著我的面吹牛警检,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播害淤,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼扇雕,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起华临,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤像屋,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后哨苛,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡币砂,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年建峭,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片决摧。...
    茶點故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡亿蒸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出掌桩,到底是詐尸還是另有隱情边锁,我是刑警寧澤,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布波岛,位于F島的核電站茅坛,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏盆色。R本人自食惡果不足惜灰蛙,卻給世界環(huán)境...
    茶點故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望隔躲。 院中可真熱鬧摩梧,春花似錦、人聲如沸宣旱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽浑吟。三九已至笙纤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間组力,已是汗流浹背省容。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留燎字,地道東北人腥椒。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓阿宅,卻偏偏與公主長得像,于是被迫代替她去往敵國和親笼蛛。 傳聞我的和親對象是個殘疾皇子洒放,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,914評論 2 355

推薦閱讀更多精彩內(nèi)容