Tính năng hỗ trợ luồng WebAssembly đã được phát hành trong Chrome 70 theo thử nghiệm theo nguồn gốc.
WebAssembly (Wasm) cho phép biên dịch mã được viết bằng C++ và các ngôn ngữ khác để chạy trên web. Một tính năng rất hữu ích của các ứng dụng gốc là khả năng sử dụng luồng – một đối tượng gốc để tính toán song song. Hầu hết các nhà phát triển C và C++ đều sẽ quen thuộc với pthreads, một API được chuẩn hoá để quản lý luồng trong một ứng dụng.
Nhóm cộng đồng WebAssembly đang nỗ lực đưa các luồng vào web để hỗ trợ các ứng dụng đa luồng thực sự. Trong nỗ lực này, V8 đã triển khai tính năng hỗ trợ cần thiết cho các luồng trong công cụ WebAssembly, có sẵn thông qua Bản dùng thử gốc. Bản dùng thử theo nguyên gốc cho phép nhà phát triển thử nghiệm các tính năng web mới trước khi được tiêu chuẩn hoá hoàn toàn. Điều này cho phép chúng tôi thu thập ý kiến phản hồi thực tế từ các nhà phát triển dũng cảm. Đây là yếu tố quan trọng để xác thực và cải thiện các tính năng mới.
Bản phát hành Chrome 70 hỗ trợ luồng cho WebAssembly. Chúng tôi khuyến khích các nhà phát triển quan tâm bắt đầu sử dụng và gửi ý kiến phản hồi cho chúng tôi.
Chuỗi tin nhắn? Còn Worker thì sao?
Các trình duyệt đã hỗ trợ tính năng song song thông qua Web Worker kể từ năm 2012 trong Chrome 4; thực tế, bạn thường nghe thấy các thuật ngữ như "trên luồng chính", v.v. Tuy nhiên, Web Worker không chia sẻ dữ liệu có thể thay đổi giữa các trình này, thay vào đó, chúng dựa vào việc truyền thông báo để giao tiếp. Trên thực tế, Chrome phân bổ một công cụ V8 mới cho mỗi trình đơn đó (gọi là một vùng tách biệt). Các vùng chứa riêng biệt không chia sẻ mã đã biên dịch cũng như đối tượng JavaScript, do đó, chúng không thể chia sẻ dữ liệu có thể thay đổi như pthread.
Mặt khác, luồng WebAssembly là các luồng có thể chia sẻ cùng một bộ nhớ Wasm. Bộ nhớ cơ bản của bộ nhớ dùng chung được thực hiện bằng SharedArrayBuffer, một loại dữ liệu gốc JavaScript cho phép chia sẻ nội dung của một ArrayBuffer đồng thời giữa các worker. Mỗi luồng WebAssembly chạy trong một Web Worker, nhưng bộ nhớ Wasm dùng chung của chúng cho phép chúng hoạt động giống như cách hoạt động trên các nền tảng gốc. Điều này có nghĩa là các ứng dụng sử dụng luồng Wasm chịu trách nhiệm quản lý quyền truy cập vào bộ nhớ dùng chung như trong mọi ứng dụng có luồng truyền thống. Hiện có nhiều thư viện mã được viết bằng C hoặc C++ sử dụng pthreads. Bạn có thể biên dịch các thư viện này sang Wasm và chạy ở chế độ luồng thực sự, cho phép nhiều lõi hoạt động trên cùng một dữ liệu cùng một lúc.
Ví dụ đơn giản
Sau đây là ví dụ về một chương trình "C" đơn giản sử dụng luồng.
#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;
}
Mã đó bắt đầu bằng hàm main()
khai báo 2 biến fg_val
và bg_val
. Ngoài ra, cả hai luồng trong ví dụ này còn có một hàm tên là fibonacci()
. Hàm main()
tạo một luồng trong nền bằng cách sử dụng pthread_create()
. Nhiệm vụ của luồng này là tính toán giá trị của dãy số fibonacci tương ứng với giá trị của biến bg_val
. Trong khi đó, hàm main()
chạy trong luồng trên nền trước sẽ tính toán giá trị này cho biến fg_val
. Sau khi luồng trong nền chạy xong, kết quả sẽ được in ra.
Biên dịch để hỗ trợ luồng
Trước tiên, bạn nên cài đặt SDK emscripten, tốt nhất là phiên bản 1.38.11 trở lên. Để tạo mã ví dụ với các luồng được bật để chạy trong trình duyệt, chúng ta cần truyền thêm một vài cờ vào trình biên dịch emcc emscripten. Dòng lệnh của chúng ta sẽ có dạng như sau:
emcc -O2 -s USE_PTHREADS=1 -s PTHREAD_POOL_SIZE=2 -o test.js test.c
Đối số dòng lệnh "-s USE_PTHREADS=1
" bật tính năng hỗ trợ tạo luồng cho mô-đun WebAssembly đã biên dịch và đối số "-s PTHREAD_POOL_SIZE=2
" sẽ yêu cầu trình biên dịch tạo một nhóm gồm hai (2) luồng.
Khi chạy, chương trình sẽ tải mô-đun WebAssembly,
tạo Web Worker cho từng luồng trong nhóm luồng, chia sẻ mô-đun
với từng worker, trong trường hợp này là 2 và các mô-đun đó sẽ được sử dụng bất cứ khi nào
có lệnh gọi đến pthread_create()
. Mỗi worker tạo thực thể cho mô-đun Wasm với cùng một bộ nhớ để cộng tác. Các thay đổi mới nhất của V8 trong phiên bản 7.0 chia sẻ mã gốc được biên dịch của các mô-đun Wasm được truyền giữa các worker, cho phép ngay cả các ứng dụng rất lớn cũng có thể mở rộng quy mô cho nhiều worker. Lưu ý rằng bạn nên đảm bảo kích thước của nhóm luồng bằng với số lượng luồng tối đa mà ứng dụng của bạn cần, nếu không quá trình tạo luồng có thể không thành công.
Đồng thời, nếu kích thước nhóm luồng quá lớn, bạn sẽ tạo ra các Worker web không cần thiết mà không làm gì cả ngoài việc sử dụng bộ nhớ.
Cách dùng thử
Cách nhanh nhất để kiểm thử mô-đun WebAssembly là bật tính năng hỗ trợ luồng WebAssembly thử nghiệm trong Chrome 70 trở lên. Chuyển đến URL about://flags
trong trình duyệt như minh hoạ dưới đây:
Tiếp theo, hãy tìm chế độ cài đặt luồng WebAssembly thử nghiệm có dạng như sau:
Thay đổi chế độ cài đặt thành Bật như minh hoạ bên dưới, sau đó khởi động lại trình duyệt.
Sau khi trình duyệt khởi động lại, chúng ta có thể thử tải mô-đun WebAssembly theo luồng bằng một trang HTML tối thiểu, chỉ chứa nội dung sau:
<!DOCTYPE html>
<html>
<title>Threads test</title>
<body>
<script src="test.js"></script>
</body>
</html>
Để thử trang này, bạn cần chạy một số loại máy chủ web và tải máy chủ đó từ trình duyệt. Thao tác này sẽ khiến mô-đun WebAssembly tải và chạy. Khi mở DevTools, bạn sẽ thấy kết quả của quá trình chạy và bạn sẽ thấy một hình ảnh kết quả như bên dưới trong bảng điều khiển:
Chương trình WebAssembly của chúng ta với các luồng đã thực thi thành công! Bạn nên thử ứng dụng theo luồng của riêng mình bằng cách làm theo các bước nêu trên.
Thử nghiệm trong thực tế bằng Bản dùng thử theo nguyên gốc
Bạn có thể thử nghiệm luồng bằng cách bật cờ thử nghiệm trong trình duyệt cho mục đích phát triển, nhưng nếu muốn kiểm thử ứng dụng trong thực tế, bạn có thể thực hiện việc này bằng thử nghiệm gốc.
Bản dùng thử theo nguyên gốc cho phép bạn dùng thử các tính năng thử nghiệm với người dùng bằng cách lấy mã thông báo kiểm thử được liên kết với miền của bạn. Sau đó, bạn có thể triển khai ứng dụng và hy vọng ứng dụng đó hoạt động trong một trình duyệt có thể hỗ trợ tính năng mà bạn đang kiểm thử (trong trường hợp này là Chrome 70 trở lên). Để nhận mã thông báo của riêng bạn nhằm chạy thử nghiệm theo nguyên gốc, hãy sử dụng biểu mẫu đăng ký tại đây.
Chúng tôi đã lưu trữ ví dụ đơn giản ở trên bằng mã thông báo thử nghiệm gốc, vì vậy, bạn có thể tự mình dùng thử mà không cần tạo bất kỳ nội dung nào.
Nếu muốn xem 4 luồng chạy song song có thể làm gì cho nghệ thuật ASCII, thì bạn cũng phải xem bản minh hoạ này!
Gửi phản hồi cho chúng tôi
Luồng WebAssembly là một nguyên hàm mới cực kỳ hữu ích để chuyển các ứng dụng sang web. Giờ đây, bạn có thể chạy các ứng dụng và thư viện C và C++ yêu cầu hỗ trợ pthreads trong môi trường WebAssembly.
Chúng tôi đang tìm kiếm ý kiến phản hồi của các nhà phát triển đang dùng thử tính năng này để có thể điều chỉnh quy trình chuẩn hoá cũng như xác thực tính hữu ích của tính năng. Cách tốt nhất để gửi ý kiến phản hồi là báo cáo vấn đề và/hoặc tham gia vào quy trình chuẩn hoá trong Nhóm cộng đồng WebAssembly.