JSRE中的多工與多執行緒

靈感桌面發表於2021-11-16

前言

​ 這幾天在愛智官網看了下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內建的事件迴圈有什麼異同,下次將會為大家通過對比進行講解。

如果以上有說的不對的,歡迎下面評論。

相關文章