前言
這幾天在愛智官網看了下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
內建的事件迴圈有什麼異同,下次將會為大家通過對比進行講解。
如果以上有說的不對的,歡迎下面評論。