Chrome 70 已在來源試用中支援 WebAssembly 執行緒。
WebAssembly (Wasm) 可將 C++ 和其他語言編寫的程式碼編譯為可在網路上執行的程式碼。原生應用程式有一項非常實用的功能,就是可以使用執行緒,這是平行運算的基本元素。大多數 C 和 C++ 開發人員都熟悉 pthreads,這是用於管理應用程式中執行緒的標準化 API。
WebAssembly 社群群組一直致力於將執行緒導入網路,以啟用真正的多執行緒應用程式。為此,V8 已在 WebAssembly 引擎中實作執行緒的必要支援,可透過來源試用取得。來源試用可讓開發人員在新的網頁功能完全標準化之前,先進行實驗。這讓我們能從勇於嘗試的開發人員收集實際意見回饋,這對驗證及改善新功能至關重要。
Chrome 70 版本支援 WebAssembly 執行緒,我們鼓勵有興趣的開發人員開始使用,並提供意見回饋。
Threads?工作站呢?
自 2012 年 Chrome 4 起,瀏覽器就透過 Web Worker 支援平行處理;事實上,我們經常聽到「在主執行緒上」等詞彙。不過,Web Worker 不會在彼此之間共用可變動資料,而是依賴訊息傳遞進行通訊。事實上,Chrome 會為每個網站分配新的 V8 引擎 (稱為隔離區)。Isolate 不會共用編譯程式碼或 JavaScript 物件,因此無法像 pthread 一樣共用可變動資料。
另一方面,WebAssembly 執行緒則是可以共用相同 Wasm 記憶體的執行緒。共用記憶體的基礎儲存空間是透過 SharedArrayBuffer 完成,這是一種 JavaScript 原始物件,可讓 worker 同時共用單一 ArrayBuffer 的內容。每個 WebAssembly 執行緒都在 Web Worker 中執行,但共用的 Wasm 記憶體可讓這些執行緒以類似原生平台的方式運作。這表示使用 Wasm 執行緒的應用程式,必須負責管理共用記憶體的存取權,就像任何傳統執行緒應用程式一樣。許多現有的 C 或 C++ 程式碼程式庫都使用 pthreads,這些程式庫可以編譯為 Wasm,並以真正的執行緒模式執行,讓更多核心同時處理相同的資料。
簡單範例
以下是使用執行緒的簡單「C」程式範例。
#include <pthread.h>
#include <stdio.h>
// Calculate Fibonacci numbers shared function
int fibonacci(int iterations) {
int val = 1;
int last = 0;
if (iterations == 0) {
return 0;
}
for (int i = 1; i < iterations; i++) {
int seq;
seq = val + last;
last = val;
val = seq;
}
return val;
}
// Start function for the background thread
void *bg_func(void *arg) {
int *iter = (void *)arg;
*iter = fibonacci(*iter);
return arg;
}
// Foreground thread and main entry point
int main(int argc, char *argv[]) {
int fg_val = 54;
int bg_val = 42;
pthread_t bg_thread;
// Create the background thread
if (pthread_create(&bg_thread, NULL, bg_func, &bg_val)) {
perror("Thread create failed");
return 1;
}
// Calculate on the foreground thread
fg_val = fibonacci(fg_val);
// Wait for background thread to finish
if (pthread_join(bg_thread, NULL)) {
perror("Thread join failed");
return 2;
}
// Show the result from background and foreground threads
printf("Fib(42) is %d, Fib(6 * 9) is %d\n", bg_val, fg_val);
return 0;
}
該程式碼以 main() 函式開頭,宣告 2 個變數 fg_val 和 bg_val。此外,還有一個名為 fibonacci() 的函式,在本例中,兩個執行緒都會呼叫這個函式。main() 函式會使用 pthread_create() 建立背景執行緒,其工作是計算與 bg_val 變數值對應的費波那契數列值。同時,在前景執行緒中執行的 main() 函式會計算 fg_val 變數。背景執行緒執行完畢後,系統會列印結果。
編譯執行緒支援
首先,您必須安裝 emscripten SDK,建議使用 1.38.11 以上版本。如要建構啟用執行緒的範例程式碼,以便在瀏覽器中執行,我們需要將幾個額外的標記傳遞至 emscripten emcc 編譯器。指令列如下所示:
emcc -O2 -s USE_PTHREADS=1 -s PTHREAD_POOL_SIZE=2 -o test.js test.c
指令列引數「-s USE_PTHREADS=1」會為編譯的 WebAssembly 模組啟用執行緒支援,而引數「-s PTHREAD_POOL_SIZE=2」則會指示編譯器產生兩個執行緒的集區。
程式執行時,系統會在背景載入 WebAssembly 模組,為執行緒集區中的每個執行緒建立 Web Worker,並與每個 Worker 共用模組 (在本例中為 2 個),每當呼叫 pthread_create() 時,就會使用這些 Worker。每個 worker 都會使用相同的記憶體例項化 Wasm 模組,以便合作。V8 在 7.0 版中的最新變更,會共用在 worker 之間傳遞的 Wasm 模組編譯原生程式碼,即使是非常大型的應用程式,也能擴充至多個 worker。請注意,執行緒集區大小應等於應用程式所需的執行緒數量上限,否則執行緒建立作業可能會失敗。同時,如果執行緒集區過大,就會建立不必要的 Web Worker,這些 Worker 只會占用記憶體,不會執行任何工作。
如何試用
如要測試 WebAssembly 模組,最快的方法是在 Chrome 70 以上版本中,開啟實驗性的 WebAssembly 執行緒支援功能。在瀏覽器中前往網址 about://flags,如下所示:
接著,找出實驗性 WebAssembly 執行緒設定,如下所示:
如下圖所示,將設定變更為「Enabled」,然後重新啟動瀏覽器。
瀏覽器重新啟動後,我們可以嘗試使用最小的 HTML 頁面載入執行緒 WebAssembly 模組,其中只包含以下內容:
<!DOCTYPE html>
<html>
<title>Threads test</title>
<body>
<script src="test.js"></script>
</body>
</html>
如要試用這個頁面,您必須執行某種形式的網路伺服器,並從瀏覽器載入。這會導致 WebAssembly 模組載入並執行。開啟開發人員工具後,您會看到執行作業的輸出內容,控制台中應會顯示類似下方的輸出圖片:
含有執行緒的 WebAssembly 程式已成功執行!建議您按照上述步驟,嘗試建立自己的執行緒應用程式。
透過來源試用進行實地測試
在瀏覽器中啟用實驗性旗標來試用執行緒,適合用於開發目的,但如果您想在實際環境中測試應用程式,可以使用來源試用。
來源試用可讓您取得與網域連結的測試權杖,與使用者一起試用實驗功能。接著,您可以部署應用程式,並預期該應用程式可在支援您測試功能的瀏覽器 (在本例中為 Chrome 70 以上版本) 中運作。如要取得自己的權杖來執行來源試用,請使用這份申請表單。
我們已使用來源試用權杖託管上述簡單範例,因此您可以自行試用,不必建構任何內容。
如果想瞭解 4 個執行緒平行執行時,能為 ASCII 藝術帶來什麼效果,請務必觀看這個示範!
提供意見
WebAssembly 執行緒是非常實用的新原始物件,可將應用程式移植到網頁。現在,您可以在 WebAssembly 環境中執行需要 pthreads 支援的 C 和 C++ 應用程式和程式庫。
我們希望試用這項功能的開發人員提供意見回饋,協助我們瞭解標準化程序,並驗證這項功能的實用性。提供意見的最佳方式是回報問題,和/或參與 WebAssembly 社群群組的標準化程序。