---
title: "Node.js 线程传二进制：复制和转移 ArrayBuffer 有什么区别"
date: 2026-11-13
slug: node-worker-transferable-buffers
canonical: https://kernelranch.com/blog/2026/11/node-worker-transferable-buffers
markdown_url: https://kernelranch.com/blog/2026/11/node-worker-transferable-buffers.md
tags: ["nodejs", "worker-threads", "binary"]
---把大二进制数据交给 Worker 时，先确定发送方是否还要读取同一块内存。默认消息会复制普通 `ArrayBuffer` 的内容。将独占的 `ArrayBuffer` 加入 transfer list 可以转移其所有权，发送方随后不能继续使用该缓冲区以及它的其他视图。

## 1. 本机复现

保存为 `worker_lab.mjs`，运行 `node worker_lab.mjs`。同一个文件在主线程发消息，在工作线程把收到的字节转成普通数组返回。

```javascript
import assert from 'node:assert/strict';
import { once } from 'node:events';
import { isMainThread, parentPort, Worker } from 'node:worker_threads';

if (!isMainThread) {
  parentPort.on('message', bytes => parentPort.postMessage(Array.from(bytes)));
} else {
  const worker = new Worker(new URL(import.meta.url));
  try {
    const copied = Uint8Array.of(1, 2, 3);
    const copyResult = once(worker, 'message');
    worker.postMessage(copied);
    copied[0] = 9;
    const [copy] = await copyResult;
    assert.deepEqual(copy, [1, 2, 3]);
    assert.equal(copied.byteLength, 3);
    console.log('copy: received', copy.join(','), 'sender[0]=', copied[0]);

    const moved = Uint8Array.of(4, 5, 6);
    const alias = moved.subarray(1);
    const moveResult = once(worker, 'message');
    worker.postMessage(moved, [moved.buffer]);
    assert.equal(moved.byteLength, 0);
    assert.equal(alias.byteLength, 0);
    const [received] = await moveResult;
    assert.deepEqual(received, [4, 5, 6]);
    console.log('transfer: received', received.join(','), 'sender lengths=',
      moved.byteLength, alias.byteLength);
  } finally {
    await worker.terminate();
  }
}
```

本地实测输出如下。

```text
copy: received 1,2,3 sender[0]= 9
transfer: received 4,5,6 sender lengths= 0 0
```

## 2. 明确谁拥有底层缓冲区

`subarray()` 创建的是视图。即便它只覆盖原始数组的一部分，转移原数组的 `buffer` 后，这个视图也会失效。如果只想把切片给 Worker，可以先用 `Uint8Array.from(view)` 分配并复制这一段，再转移新数组的缓冲区。这个步骤仍有一次切片复制，开销取决于数据量。

Node.js 的 `Buffer` 可能引用内存池。Node 文档指出，`Buffer.from()`、`Buffer.allocUnsafe()` 等创建的实例如果来自池，其底层缓冲区不能作为独占内存交接，复制还可能涉及整个池。来源不明确时，为要发送的字节创建独立的普通 `Uint8Array`。只有当多个线程确实要并发访问同一份内存时，再考虑 `SharedArrayBuffer` 并设计同步机制。

## 小结

继续使用原数据就复制，能够交出独占底层内存就转移。发送前确认是否还有视图引用同一缓冲区。

## 数据来源与检索时间

检索时间 2026-11-13。

| 来源 | URL | 对应内容 |
|---|---|---|
| Node.js worker_threads | https://nodejs.org/docs/latest-v24.x/api/worker_threads.html#portpostmessagevalue-transferlist | 消息复制、转移列表及共享缓冲区 |
| Node.js TypedArray 和 Buffer | https://nodejs.org/docs/latest-v24.x/api/worker_threads.html#considerations-when-transferring-typedarrays-and-buffers | 共享视图失效与 Buffer 内存池 |

## 核验说明

- Windows、Node.js v24.19.0 在真实工作线程里验证复制快照、转移结果和两个视图失效。样本仅三字节，未测吞吐量，也未操作内存池中的 Buffer。
