How Browsers Work · Part 20 — Threading Deep: Workers, SharedArrayBuffer & Atomics
Đa luồng thật trên web: Web Workers, transferable vs SharedArrayBuffer, Atomics để đồng bộ, và cross-origin isolation (COOP/COEP) mở khoá bộ nhớ chia sẻ cùng timer phân giải cao.
JavaScript chạy đơn luồng trên mỗi context (Phần 7), nhưng nền tảng web có đa luồng thật qua Web Workers. Phần này đi sâu vào cách worker giao tiếp, cách chia sẻ bộ nhớ thật sự với SharedArrayBuffer, dùng Atomics để đồng bộ, và vì sao những thứ đó bị khoá sau cross-origin isolation.
1. Worker = execution context riêng, có thể chạy đồng thời
Mỗi worker có global scope/event loop riêng và không truy cập DOM của document. Engine có thể chạy worker đồng thời với main thread trên worker thread; chi tiết ánh xạ tới OS thread thuộc implementation. JS object thông thường không chia sẻ trực tiếp giữa các context — giao tiếp qua message hoặc shared memory có kiểm soát.
Main thread (window) Worker thread
event loop riêng event loop riêng
heap riêng ◄─postMessage─► heap riêng
có DOM KHÔNG có DOM
Ba loại worker:
| Loại | Phạm vi | Dùng cho |
|---|---|---|
| Dedicated Worker | một trang sở hữu | tính toán nặng riêng cho trang |
| Shared Worker | nhiều context cùng origin/storage partition | chia sẻ trạng thái/kết nối khi được hỗ trợ |
| Service Worker | lifecycle/proxy mạng riêng (Phần 10) | offline, cache, push; không phải compute worker sống mãi |
const w = new Worker(new URL('./worker.js', import.meta.url), { type: 'module' });
w.postMessage({ cmd: 'crunch', data });
w.onmessage = (e) => render(e.data);
2. postMessage: structured clone vs transfer
Mặc định, postMessage serialize/deserialize graph dữ liệu bằng structured clone. Với object/buffer lớn, việc tạo bản sao có thể tốn CPU và memory.
structured clone: [data] ──copy──► [data'] (cả hai bên đều có bản)
transfer: [buffer] ──move──► [buffer] (bên gửi bị detach)
Transferable (ArrayBuffer, MessagePort, ImageBitmap, OffscreenCanvas…) có semantics chuyển ownership: object phía gửi bị detach/không dùng lại được, còn phía nhận có object mới trỏ tới backing resource đã chuyển. Cơ chế này tránh structured-clone toàn bộ payload đắt tiền; đừng phụ thuộc vào chi tiết “physical zero-copy” của implementation.
const buf = new ArrayBuffer(64 * 1024 * 1024);
worker.postMessage({ buf }, [buf]); // transfer list → ownership chuyển đi
// buf.byteLength === 0 ở đây: đã mất quyền
Dùng transfer cho buffer lớn để tránh chi phí clone. Nhưng transfer vẫn là chuyển, không phải chia sẻ — chỉ một bên giữ tại một thời điểm.
3. SharedArrayBuffer — bộ nhớ chia sẻ thật
SharedArrayBuffer (SAB) là vùng nhớ nhiều luồng cùng đọc/ghi đồng thời — không copy, không transfer. Đây là chia sẻ bộ nhớ thật như trong ngôn ngữ đa luồng.
const sab = new SharedArrayBuffer(1024);
const view = new Int32Array(sab);
worker.postMessage({ sab }); // KHÔNG transfer — cả hai cùng thấy cùng vùng nhớ
// Dùng Atomics để publication/ordering rõ ràng giữa các agent
Atomics.store(view, 0, 42);
Các byte thực sự được chia sẻ, nhưng câu “luồng kia thấy ngay” không phải memory model. Nhiều agent đọc/ghi không phối hợp tạo race hoặc ordering khó đoán; dùng Atomics cho synchronization/publication.
4. Atomics — đọc/ghi nguyên tử & đồng bộ
Atomics cung cấp thao tác nguyên tử trên integer typed array được backed bởi SAB, cùng cơ chế chờ/đánh thức để xây primitive đồng bộ.
const view = new Int32Array(sab);
// Thao tác nguyên tử (an toàn khi nhiều luồng cùng đụng)
Atomics.add(view, 0, 1); // tăng nguyên tử
const v = Atomics.load(view, 0); // đọc nguyên tử
// worker.js: luôn kiểm tra điều kiện trong vòng lặp
while (Atomics.load(view, 0) === 0) {
Atomics.wait(view, 0, 0); // ngủ nếu giá trị vẫn bằng expected (0)
}
Ở agent khác (ví dụ main thread), cập nhật state trước rồi mới notify:
Atomics.store(view, 0, 1);
Atomics.notify(view, 0, 1);
Atomics.wait() chỉ hợp lệ trên agent được phép block (web main thread sẽ throw). Atomics.notify() chỉ đánh thức waiter; nó không tự thay đổi dữ liệu, nên waiter phải kiểm tra lại điều kiện. Khi được hỗ trợ, Atomics.waitAsync() dùng được ở main thread mà không block; API trả { async, value }, trong đó value là Promise hoặc kết quả tức thì:
const result = Atomics.waitAsync(view, 0, 0);
await result.value;
5. Cross-origin isolation — vì sao SAB bị khoá
Sau Spectre, browser hạn chế SharedArrayBuffer và timer chính xác vì chúng làm timing side-channel mạnh hơn. Trên web, cần secure context và trạng thái cross-origin isolated. Cặp header phổ biến, interoperable nhất là:
Cross-Origin-Opener-Policy: same-origin (COOP — tách browsing context group)
Cross-Origin-Embedder-Policy: require-corp (COEP — chặn tài nguyên cross-origin chưa opt-in)
if (crossOriginIsolated) {
// mới được dùng SharedArrayBuffer, performance.measureUserAgentSpecificMemory,
// và timer performance.now() phân giải cao hơn
}
Mô hình: COOP tách top-level document sang browsing context group phù hợp; COEP chỉ cho nhúng cross-origin resource đã opt in qua CORP hoặc CORS. Một số browser còn hỗ trợ COEP: credentialless, nhưng support/trade-off khác nhau. Đây là web opt-in policy, bổ sung chứ không đồng nhất với Site Isolation nội bộ ở Phần 21. Hãy kiểm tra kết quả bằng self.crossOriginIsolated và audit popup/OAuth/third-party embeds trước khi deploy.
6. Khi nào dùng gì
Tính toán nặng, dữ liệu copy được → Worker + structured clone
Buffer lớn, gửi một chiều → Worker + transfer ownership
Nhiều luồng cùng truy cập state nóng → SharedArrayBuffer + Atomics (cần COOP/COEP)
Vẽ canvas khi main bận → OffscreenCanvas trong worker (Phần 19)
Chia sẻ message giữa nhiều tab → SharedWorker / BroadcastChannel (cùng partition)
Đừng vội dùng SAB: với hầu hết ứng dụng, worker + transfer là đủ và đơn giản hơn nhiều. SAB/Atomics dành cho wasm threads, xử lý audio/video realtime, hoặc cấu trúc dữ liệu chia sẻ hiệu năng cao.
7. Tóm tắt
- Worker cho execution context/event loop riêng có thể chạy đồng thời; dedicated/shared/service worker có lifecycle và mục đích khác nhau.
postMessagedùng structured clone; transferable chuyển ownership và detach phía gửi, tránh clone payload đắt tiền.SharedArrayBufferchia sẻ bộ nhớ thật giữa các luồng (không copy/transfer) → cần đồng bộ.Atomicscung cấp operation/ordering nguyên tử;waitcần vòng lặp điều kiện và không được block main thread, cònwaitAsyncphải feature-detect.- SAB trên web cần secure context + cross-origin isolation; cặp phổ biến là
COOP: same-origin+COEP: require-corp, kiểm traself.crossOriginIsolated. - Chọn công cụ theo nhu cầu: copy → clone, buffer lớn một chiều → transfer, state nóng đa luồng → SAB+Atomics.
Phần tiếp theo: Process model deep — Site Isolation và OOPIF, Back/Forward Cache (BFCache), prerender/speculation rules, và Page Lifecycle API điều phối tài nguyên giữa các tab.