JSRE中的多任務與多執行緒
- 2021 年 11 月 16 日
- 筆記
- EdgerOS, javascript, NodeJs, 技術分享
前言
這幾天在愛智官網看了下JSRE
其他的Api
,看了一個比較有意思的模組 – 多任務模組task
,大致看了下他們的介面說明和案例,感覺和多執行緒差不多,然後就準備去看下實現方式,找了很久沒有找到源碼(╬ ̄皿 ̄),問了他們那邊工作人員才知道目前源碼還沒有開放出來,那我也就只能 wait, wait …
凌晨3點半的我又醒來繼續敲程式碼了,可信度看人品!!!
在沒有得到源碼加持的我,只能輕裝上陣,這裝備感覺承受不住你們的第一輪筆伐 … 希望在座的各位可以做個人,啊不,是做個猿(媛)!
多任務介紹
鄙人經過九牛一毛之力,終於給大家帶來了第一手資訊。據可靠情報得知(PS:其實也就從他們官網直接複製了一點官方介紹過來(≖ᴗ≖)✧):
JSRE中每一個創建的Task都是作業系統中的一個獨立執行緒,作業系統可以根據調度策略獨立調度,用來提高應用程式的並行性。Node.js等運行時平台雖然通過多執行緒非同步代理並行化,但核心程式進程無法並行化,程式設計師可控性太低(這點我比較贊同),沒有可控策略。除此之外,多任務模組還提供了更好的應用程式碼解耦。應用模組分離和開發,更容易構建更複雜的大規模應用。
花了我3秒的時間敲出了上面的一大片內容,感慨自己從三歲就開始敲鍵盤的努力沒有白費!(ಥ﹏ಥ)。
我們都知道Node.js
是單執行緒,但這僅僅指js
執行主執行緒是單執行緒,其他非同步IO和事件驅動相關的執行緒通過libuv
來實現內部的執行緒池和執行緒調度,本身只負責不斷的往返調度,並沒有進行真正的I/O操作,從而實現非同步非阻塞I/O。
【問題】:這邊說JSRE
中每個task都是一個獨立的執行緒,難道JSRE
本身就支援多執行緒的嘛?
【答】:不用想了,我自己也不是很清楚,在官網沒有找到一個準確的答案,那我們就接著繼續實踐出真知!
實例測試
1、同步執行
- 測試程式碼:
// main.js
...
console.info('start');
// 模擬3s耗時操作
let t = 0;
while (t < 3000) {
console.warn('wait...');
sys.sleep(1000);
t += 1000;
}
console.info('end');
...
- 運行結果:
我們在入口文件main.js
中加入以上測試程式碼,我們都知道js
程式碼運行階段都是從上往下開始順序執行的,我們在主執行緒中添加了一段3秒的阻塞程式碼,當然在實際項目中也許一個服務啟動時耗時更多。
從VSCode
輸出窗口中我們可以看到日誌是順序列印的,在列印end
之前等待了3秒,使得主程式阻塞了3秒,這時候給用戶的體驗就會很卡頓。阻塞的3秒對於後面主執行緒的程式碼執行並沒有任何影響,實際項目中也許是開啟了一些應用程式的其他附加服務。
下面我再用多任務模組處理一下阻塞程式碼。
2、多任務執行
- 測試程式碼:
// main.js
...
console.info('start');
// 開啟子任務
new Task('./task.js');
console.info('end');
...
// task.js
let t = 0;
while (t < 3000) {
console.warn('wait...');
sys.sleep(1000);
t += 1000;
}
- 運行結果:
在上面測試程式碼程式碼中我們將3秒的阻塞程式碼放在了新建的task.js
文件中,在main.js
中我們通過Task
模組實例化了一個多任務實例,參數為task.js
的文件地址,這時候運行程式碼我們可以看到end
欄位的列印並不受多任務實例中的阻塞程式碼影響,這樣就不會對主執行緒運行造成不必要的阻塞。而在Node.js
中可以通過非同步I/O交給內部執行緒池、工作執行緒或者開啟子進程進行處理。
數據通訊
我們都知道執行緒之間是並行運行的,一個執行緒是無法直接訪問其他執行緒內部數據的,按照官網的說法,每一個多任務就是一個執行緒,那多任務之間的數據應該也是各自維護,隔離開的。那麼多任務之間如何能工進行數據通訊呢?這邊在愛智開發手冊上看到有很多中方式去進行多任務間通訊。其中有個比較有特點的是一個叫SyncTable
的共享映射資料庫,該模組具體的作用個人感覺應該是js
模組間通訊(純屬個人意見,錯了勿噴!)。
說岔了,言歸正傳!我們看一下這邊多任務的通訊方式之一:訊號槽通訊(SigSlot
)。
據官網介紹:
SigSlot 是一個事件驅動的非同步通訊組件,支援多任務和多進程。如果您需要多進程支援,則必須啟動 JSRE(全局訊號槽)並-g在啟動進程時使用選項啟用當前進程 GSS 支援。在 EdgerOS 中,來自同一供應商的應用程式可以使用 GSS 功能相互訂閱和發布消息。
SigSlot
是典型的訂閱和發布通訊機制。基於 SigSlot
,可以輕鬆進行多任務解耦。每個任務都是獨立設計的,大大降低了應用開發的難度。
下面直接來使用一下這個模組:
-
測試程式碼
// main.js ... const SigSlot = require('sigslot'); const testSlot = new SigSlot('test'); testSlot.slot('task', (msg) => { console.log('main: ', msg); testSlot.emit('main', 'main to task'); }) new Task('./task.js'); ...
// task.js const SigSlot = require('sigslot'); const testSlot = new SigSlot('test'); testSlot.slot('main', (msg) => { console.log('task: ', msg); }) testSlot.emit('task', 'task to main'); require('iosched').forever();
-
運行結果:
從運行結果中可以看到在JSRE
中,多任務之間可以通過訊號槽進行數據的相互傳遞,意味著多任務是可以進行數據通訊的,主要依賴於發布訂閱模式來實現模組間通訊的功能。
其實,最開始結果是只列印了main.js
訂閱的task
事件,而mian.js
中發布的main
事件在task.js
中並沒有進行列印,一頭霧水的我對照官網用法檢查了好幾遍,沒發現寫法有什麼錯誤,寫法問題就直接排除了。定位到應該是事件處理沒有觸發。
這時候我注意到了這個iosched
這個模組,經過一番查找,終於知道了原因。
該模組為JSRE
的核心模組之一,簡單理解為就是處理非同步I/0事件調度。
如果想要是js
模組始終執行非同步事件循環,可以顯示調用該模組的forever
方法去實現(require('iosched').forever()
),這點和Node.js
有著很大的差異,為什麼JSRE
要這樣處理?可惜這個不是今天的重點,後面也會去介紹一下該模組。
多任務總結
關於JSRE
中的多任務模組個人感覺還是很不錯的,
首先是這種多任務的寫法,比較直觀,個人比較推薦的;
其次就是多任務可控,JSRE
針對該模組還提供了很多的其他的介面供開發者進行任務控制。
上圖就是個人從使用上最直觀的感受,至於底層多任務的實現以及JSRE
本身是不是多執行緒機制或許只有等開源後方可知曉。這次就扯到這裡就可以了,上面關於JSRE
的非同步事件循環機制和Node.js
內置的事件循環有什麼異同,下次將會為大家通過對比進行講解。
如果以上有說的不對的,歡迎下面評論。