SharedArrayBuffer trong JavaScript
Bài viết này giải thích về SharedArrayBuffer
trong JavaScript.
Chúng tôi sẽ cung cấp một giải thích chi tiết về những điều cơ bản của SharedArrayBuffer
, cách sử dụng nó, các trường hợp sử dụng cụ thể và các cân nhắc về bảo mật.
YouTube Video
SharedArrayBuffer trong JavaScript
SharedArrayBuffer
là một công cụ mạnh mẽ trong JavaScript để chia sẻ bộ nhớ giữa các luồng. Đặc biệt khi kết hợp với Web Workers, nó cho phép xử lý song song, làm cho nó hiệu quả cho các tác vụ tính toán nặng và các ứng dụng yêu cầu khả năng xử lý theo thời gian thực.
SharedArrayBuffer là gì?
SharedArrayBuffer
cung cấp một bộ nhớ đệm trong JavaScript cho phép chia sẻ dữ liệu nhị phân giữa nhiều luồng (chủ yếu là Web Workers). Một ArrayBuffer
thông thường yêu cầu sao chép giữa luồng chính và các worker, nhưng SharedArrayBuffer
cho phép chia sẻ bộ nhớ trực tiếp mà không cần sao chép, qua đó cải thiện đáng kể hiệu suất.
Đặc điểm
- Bộ nhớ dùng chung Nó cho phép nhiều luồng cùng làm việc trên một vùng nhớ chung.
- Cải thiện hiệu suất Vì có thể bỏ qua quá trình sao chép, chi phí xử lý được giảm đáng kể khi xử lý lượng dữ liệu lớn.
- Đồng bộ hóa luồng
Bạn có thể sử dụng nó kết hợp với
Atomics
để đồng bộ hóa nhằm tránh xảy ra xung đột khi truy cập bộ nhớ.
Ví dụ sử dụng cơ bản
1// Create a 16-byte shared memory
2const sharedBuffer = new SharedArrayBuffer(16);
3
4// Treat it as an Int32Array
5const sharedArray = new Int32Array(sharedBuffer);
6
7// Set a value
8sharedArray[0] = 42;
9
10console.log(sharedArray[0]); // 42
Trong ví dụ này, chúng ta tạo một khu vực bộ nhớ 16 byte bằng SharedArrayBuffer
và xử lý khu vực bộ nhớ đó như một Int32Array
. Bộ nhớ đệm này có thể được chia sẻ giữa nhiều luồng.
Sử dụng với Web Workers
Giá trị thực sự của SharedArrayBuffer
được thể hiện khi sử dụng cùng với Web Workers. Đoạn code sau đây là một ví dụ về cách sử dụng bộ nhớ chia sẻ giữa luồng chính và một worker.
Trên luồng chính
1// Create a shared buffer
2const sharedBuffer = new SharedArrayBuffer(16);
3const sharedArray = new Int32Array(sharedBuffer);
4
5// Create a worker
6const worker = new Worker('worker.js');
7
8// Pass the shared buffer to the worker
9worker.postMessage(sharedBuffer);
10
11// Modify the memory
12// Output : Main thread: 100
13sharedArray[0] = 100;
14console.log("Main thread: ", sharedArray[0]);
Trong Worker (worker.js
)
1// worker.js
2self.onmessage = function(event) {
3 // Use the received shared buffer
4 const sharedArray = new Int32Array(event.data);
5
6 // Read the contents of the memory
7 // Output : Worker thread: 100
8 console.log("Worker thread: ", sharedArray[0]);
9
10 // Change the value
11 sharedArray[0] = 200;
12};
- Trong ví dụ này, luồng chính tạo ra một bộ đệm chia sẻ và truyền nó tới worker. Worker có thể truy cập bộ đệm này để đọc và sửa đổi các giá trị. Bằng cách này, dữ liệu có thể được chia sẻ giữa các luồng mà không cần sao chép.
Xác nhận cập nhật hai chiều
Bằng cách sử dụng SharedArrayBuffer
, cả luồng chính và các worker đều có thể đọc và ghi vào cùng một vùng nhớ, qua đó cho phép xác nhận cập nhật hai chiều. Dưới đây là ví dụ trong đó luồng chính thiết lập một giá trị, worker thay đổi giá trị đó, sau đó luồng chính kiểm tra sự cập nhật.
Trên luồng chính
1// Create a shared buffer
2const sharedBuffer = new SharedArrayBuffer(16);
3const sharedArray = new Int32Array(sharedBuffer);
4
5// Create a worker
6const worker = new Worker('worker.js');
7
8// Pass the shared buffer to the worker
9worker.postMessage(sharedBuffer);
10
11// Set initial value
12// Output : Main thread initial: 100
13sharedArray[0] = 100;
14console.log("Main thread initial:", sharedArray[0]);
15
16// Listen for worker confirmation
17worker.onmessage = () => {
18 // Output : Main thread after worker update: 200
19 console.log("Main thread after worker update:", sharedArray[0]);
20};
Trong Worker (worker.js
)
1// worker.js
2self.onmessage = function(event) {
3 const sharedArray = new Int32Array(event.data);
4
5 // Read initial value
6 // Output : Worker thread received: 100
7 console.log("Worker thread received:", sharedArray[0]);
8
9 // Update the value
10 sharedArray[0] = 200;
11
12 // Notify main thread
13 self.postMessage("Value updated");
14};
- Trong ví dụ này, trước tiên luồng chính ghi giá trị
100
, sau khi worker đọc được giá trị này thì nó sẽ ghi đè lại thành200
. Sau đó, worker sẽ thông báo cho luồng chính, và luồng chính sẽ đọc lại bộ nhớ dùng chung để xác nhận cập nhật. Bằng cách kết hợp việc thông báo, ta có thể xác nhận cập nhật hai chiều.
Đồng bộ hóa với Atomics
Khi sử dụng bộ nhớ chia sẻ, cần thận trọng với các điều kiện đua dữ liệu và sự không nhất quán. Khi nhiều luồng truy cập cùng một bộ nhớ đồng thời, xung đột có thể xảy ra. Để ngăn chặn điều này, JavaScript sử dụng đối tượng Atomics
để đồng bộ hóa.
Ví dụ, để tăng an toàn một bộ đếm bằng nhiều luồng, bạn có thể sử dụng Atomics
để tránh xung đột.
1const sharedBuffer = new SharedArrayBuffer(16);
2const sharedArray = new Int32Array(sharedBuffer);
3
4// Increment the counter
5Atomics.add(sharedArray, 0, 1);
6
7console.log(Atomics.load(sharedArray, 0)); // 1
Atomics.add
tăng giá trị tại một chỉ số cụ thể một cách nguyên tử và trả về giá trị mới. Hoạt động này được đảm bảo không có xung đột với các luồng khác. Atomics.load
cũng được sử dụng để đọc giá trị một cách an toàn từ bộ nhớ chia sẻ.
Chờ đợi và thông báo bằng Atomics.wait
và Atomics.notify
Khi sử dụng SharedArrayBuffer
, có những trường hợp worker cần phải chờ đến khi một điều kiện nào đó được đáp ứng, và khi worker khác hoàn thành điều kiện đó thì phải thông báo cho worker đang chờ. Trong trường hợp như vậy, Atomics.wait
và Atomics.notify
rất hữu ích.
Atomics.wait
sẽ chặn một luồng cho đến khi giá trị tại chỉ số xác định trong bộ nhớ dùng chung thay đổi, còn Atomics.notify
sẽ thông báo cho các luồng đang chờ rằng họ có thể tiếp tục. Điều này giúp các worker có thể chờ đợi và thông báo an toàn với nhau. Tuy nhiên, Atomics.wait
không thể được sử dụng trên luồng chính và chỉ có sẵn trong các worker.
1// Create a shared buffer (1 Int32 slot is enough for signaling)
2const sharedBuffer = new SharedArrayBuffer(4);
3const sharedArray = new Int32Array(sharedBuffer);
4
5// Create workers with names
6const waiter = new Worker('worker.js', { name: 'waiter' });
7const notifier = new Worker('worker.js', { name: 'notifier' });
8
9// Pass the shared buffer to both
10waiter.postMessage(sharedBuffer);
11notifier.postMessage(sharedBuffer);
12
13// Listen for messages
14waiter.onmessage = (event) => {
15 console.log(`[Main] Message from waiter:`, event.data);
16};
17notifier.onmessage = (event) => {
18 console.log(`[Main] Message from notifier:`, event.data);
19};
- Trên luồng chính, một
SharedArrayBuffer
được tạo ra như bộ nhớ chia sẻ và được chuyển đổi thành mộtInt32Array
chỉ với một phần tử. Khe số nguyên duy nhất này được sử dụng như một tín hiệu để đồng bộ hóa giữa các worker. Tiếp theo, hai worker được tạo ra và mỗi người được gán một vai trò sử dụng thuộc tínhname
:waiter
(vai trò chờ) vànotifier
(vai trò thông báo). Cuối cùng, bộ đệm chia sẻ được chuyển đến cả hai worker và các trình xử lýonmessage
được thiết lập để các thông điệp gửi từ mỗi worker có thể được nhận.
Trong Worker (worker.js
)
1// worker.js
2onmessage = (event) => {
3 const sharedArray = new Int32Array(event.data);
4
5 if (self.name === 'waiter') {
6 postMessage('Waiter is waiting...');
7 // Wait until notifier signals index 0
8 Atomics.wait(sharedArray, 0, 0);
9 postMessage('Waiter was notified!');
10 }
11
12 if (self.name === 'notifier') {
13 postMessage('Notifier is preparing...');
14 setTimeout(() => {
15 // Notify waiter after 2 seconds
16 Atomics.store(sharedArray, 0, 1);
17 Atomics.notify(sharedArray, 0, 1);
18 postMessage('Notifier has sent the signal!');
19 }, 2000);
20 }
21};
22// Output
23// [Main] Message from waiter: Waiter is waiting...
24// [Main] Message from notifier: Notifier is preparing...
25// [Main] Message from notifier: Notifier has sent the signal!
26// [Main] Message from waiter: Waiter was notified!
- Trong ví dụ này, worker
waiter
sẽ ở trạng thái chờ đợi sử dụngAtomics.wait
miễn là giá trị tại chỉ số0
là0
. Mặt khác, khi workernotifier
thay đổi giá trị thành123
bằngAtomics.store
và gọiAtomics.notify
, workerwaiter
sẽ tiếp tục và có thể lấy được giá trị đã cập nhật. Với điều này, có thể đạt được việc chờ đợi và thông báo giữa các luồng một cách hiệu quả và an toàn.
Các trường hợp sử dụng của SharedArrayBuffer
SharedArrayBuffer
đặc biệt hữu ích trong các trường hợp sử dụng sau:.
- Xử lý thời gian thực Nó phù hợp cho các ứng dụng yêu cầu độ trễ thấp như xử lý âm thanh, video hoặc game engine, nơi dữ liệu cần được chia sẻ lập tức giữa các luồng.
- Tính toán song song
Khi xử lý lượng dữ liệu lớn đồng thời với nhiều luồng, việc sử dụng
SharedArrayBuffer
giúp tránh sao chép bộ nhớ và có thể cải thiện hiệu suất. - Học máy (Machine Learning) Bằng cách song song hóa các công việc như tiền xử lý dữ liệu và suy luận, việc tính toán trở nên hiệu quả hơn.
Các cân nhắc về bảo mật
SharedArrayBuffer
là một tính năng mạnh mẽ, nhưng nó cũng mang theo những rủi ro bảo mật. Cụ thể, lo ngại về các cuộc tấn công kênh bên như Spectre
đã tạm thời dừng việc hỗ trợ tính năng này. Để giảm bớt lỗ hổng này, các trình duyệt đã triển khai các biện pháp sau:.
- Cô lập trang web (Site Isolation)
Các trang web cho phép sử dụng
SharedArrayBuffer
sẽ chạy trong một tiến trình hoàn toàn tách biệt với các trang web khác. - Chính sách tài nguyên đa nguồn gốc (Cross-Origin Resource Policy)
Để sử dụng
SharedArrayBuffer
, bạn cần thiết lập đúng các headerCross-Origin-Opener-Policy
vàCross-Origin-Embedder-Policy
.
Ví dụ, bằng cách thiết lập các tiêu đề như sau, việc sử dụng SharedArrayBuffer
sẽ trở nên khả thi:.
1Cross-Origin-Opener-Policy: same-origin
2Cross-Origin-Embedder-Policy: require-corp
Điều này ngăn chặn tài nguyên bên ngoài can thiệp vào nội dung hiện tại và tăng cường bảo mật.
Tóm tắt
SharedArrayBuffer
là một công cụ rất mạnh mẽ để chia sẻ bộ nhớ giữa nhiều luồng. Đây là một công nghệ thiết yếu để cải thiện hiệu suất, và hiệu quả của nó đặc biệt rõ ràng trong các lĩnh vực xử lý thời gian thực và tính toán song song. Tuy nhiên, nó cũng liên quan đến các rủi ro bảo mật, vì vậy cấu hình và đồng bộ hóa chính xác rất quan trọng.
Bằng cách sử dụng SharedArrayBuffer
, bạn có thể xây dựng các ứng dụng web tiên tiến hơn và hiệu suất cao hơn.
Bạn có thể làm theo bài viết trên bằng cách sử dụng Visual Studio Code trên kênh YouTube của chúng tôi. Vui lòng ghé thăm kênh YouTube.