在 Chrome 70 版中,WebAssembly 執行緒支援功能已在來源試用階段推出。
WebAssembly (Wasm) 可針對以 C++ 和其他語言編寫的程式碼編譯,並在網路上執行。原生應用程式的其中一項實用功能是可使用執行緒,這是平行運算的基礎。大部分的 C 和 C++ 開發人員都會熟悉 pthreads,這是在應用程式中用於管理執行緒的標準化 API。
WebAssembly 社群群組一直致力於將執行緒遷移至網路,以實現真正的多執行緒應用程式。為此,V8 已在 WebAssembly 引擎中針對執行緒導入必要支援,我們會透過來源試用提供這項支援。來源試用可讓開發人員在新的網頁功能完全標準化前先行實驗。這可讓我們收集無害的開發人員實際意見回饋,這對於驗證及改善新功能來說至關重要。
Chrome 70 版支援 WebAssembly 的執行緒,我們也鼓勵感興趣的開發人員開始使用這些執行緒並提供意見回饋。
執行緒?那 worker 呢?
自 2012 年起,瀏覽器在 Chrome 4 中透過 Web Worker 支援平行處理功能;事實上,瀏覽器之間不會出現「在主執行緒上」等字詞。然而,網路工作站不會在兩者之間共用可變動資料,而是依賴訊息傳遞來通訊。事實上,Chrome 會為每個 API 分配一個新的 V8 引擎 (稱為「隔離模式」)。隔離時不會共用編譯的程式碼和 JavaScript 物件,因此無法分享 pthread 等可變動資料。
另一方面,WebAssembly 執行緒是可以共用相同的 Wasm 記憶體的執行緒。共用記憶體的基礎儲存空間是透過 SharedArrayBuffer 取得,這是一種 JavaScript 原始版本,可讓工作站在工作站之間並行共用單一 ArrayBuffer 的內容。每個 WebAssembly 執行緒都會在網路工作站中執行,但共用的 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
」會告知編譯器產生兩個 (2) 個執行緒集區。
程式執行時,會在背景載入 WebAssembly 模組,為執行緒集區中的每個執行緒建立網路工作站,與各個工作站共用該模組 (在本例中為 2),而會在呼叫 pthread_create()
時使用這些模組。每個工作站都會使用相同的記憶體執行個體化 Wasm 模組,使其可以合作。V8 最新的 7.0 版變更分享了經過編譯的 Wasm 模組原生程式碼,這類程式碼會在工作站之間傳遞,讓非常大型的應用程式能夠擴充到許多工作站。請注意,請確保執行緒集區大小等於應用程式所需的執行緒數量上限,否則可能無法建立執行緒。與此同時,如果執行緒集區的大小太大,就會建立不必要的 Web Worker,而網路工作站只會專心處理記憶體。
試用方法
如要測試 WebAssembly 模組,最快的方式是在 Chrome 70 以上版本中啟用實驗性 WebAssembly 執行緒支援。在瀏覽器中前往網址 about://flags
,如下所示:
接著,請找出如下所示的實驗性 WebAssembly 執行緒設定:
將設定變更為「已啟用」 (如下所示),然後重新啟動瀏覽器。
瀏覽器重新啟動後,我們可以嘗試透過最小的 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 社群群組中的標準化程序。