消息传递让并发模块通过发送不可变消息来交互,避免了共享可变数据的同步难题。把它从进程内 Worker 延伸到网络上的客户端/服务器,就构成了现代 Web 应用的骨架。
消息传递并不能让并发变得「没有 bug」,但它把同步的复杂度集中到了消息边界上,更容易推理。
TypeScript(Node.js)用 worker_threads 提供工作线程。主线程与 Worker 通过 postMessage 发送消息,通过 parentPort.on('message', callback) 接收消息。
// 主线程
import { Worker } from 'worker_threads';
const worker = new Worker('./worker.js');
worker.postMessage({ type: 'add', a: 2, b: 3 });
worker.on('message', (result) => console.log(result)); // 5
// worker.js
import { parentPort } from 'worker_threads';
parentPort!.on('message', (msg) => {
if (msg.type === 'add') parentPort!.postMessage(msg.a + msg.b);
});
不同种类的消息通常用可辨识联合(discriminated union)表达,以一个 type 字段区分:
type Msg =
| { type: 'add'; a: number; b: number }
| { type: 'sub'; a: number; b: number }
| { type: 'result'; value: number };
Worker 通过事件监听器接收消息。parentPort.on('message', callback) 注册一个异步回调,每当有新消息到达时调用。
如何优雅地让 Worker 停下来?常用手法是发送一条特殊的毒丸消息(poison pill)。Worker 收到毒丸后,清理资源并退出。
// 主线程发送毒丸
worker.postMessage({ type: 'stop' });
// Worker 处理毒丸
parentPort!.on('message', (msg) => {
if (msg.type === 'stop') process.exit(0);
});
消息传递并不能消除竞态条件。如果两个客户端同时向服务器请求同一资源,服务器处理消息的顺序仍然会影响结果。只是这类竞态更容易被观察和控制。
客户端/服务器(client/server):客户端发送请求,服务器处理请求并返回响应。这是网络通信的基本设计模式。
www.mit.edu),通过 DNS 翻译成 IP 地址。HTTP 是万维网的语言。一个 HTTP 请求包含:
/api/users/42。?fields=name,email。200 OK、404 Not Found,响应体通常是 JSON。用 Express 可以快速创建路由:
import express from 'express';
const app = express();
app.get('/api/users/:id', (req, res) => {
const id = Number(req.params.id);
res.json({ id, name: 'Ada' });
});
app.listen(3000, () => console.log('服务器启动于 3000 端口'));