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ộtInt32Arraychỉ 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
waitersẽ ở trạng thái chờ đợi sử dụngAtomics.waitmiễn là giá trị tại chỉ số0là0. Mặt khác, khi workernotifierthay đổi giá trị thành123bằngAtomics.storevà gọiAtomics.notify, workerwaitersẽ 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
SharedArrayBuffergiú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
SharedArrayBuffersẽ 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-Policyvà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.