数据中心里的并发编程

数据中心里的并发编程

2025 南京大学《操作系统原理》
数据中心里的并发编程

数据中心:互联网 & AI 时代的幕后英雄

乌兰察布云谷:全国最低电价 + 全年 10 个月自然风冷

center

2025 南京大学《操作系统原理》
数据中心里的并发编程

数据中心程序:特点

“A network of computing and storage resources that enable the delivery of shared applications and data.” (CISCO)

以海量分布式数据 (存储) 为中心

  • 实时的 “小数据处理”
    • 内容分发、用户认证、视频直播、弹幕……
  • 离线的 “大数据处理”
    • 内容索引、数据挖掘……

我们的生活离不开数据中心应用

  • 搜索、社交、支付、游戏……
2025 南京大学《操作系统原理》
数据中心里的并发编程

以及,为 AI 提供支持

center

  • 基础模型的 “下半场”: 调用服务、执行代码、生成图像……
2025 南京大学《操作系统原理》
数据中心里的并发编程

数据中心里的并发编程

高吞吐 (QPS) & 低延迟的事件处理

  • 处理事件可能需要读写持久存储或请求网络上的服务
    • 延迟不确定
  • 线程维护和上下文切换都会带来开销

假设有数千/数万个请求同时到达服务器……

  • “Denial of Service, DoS”
    • 全国的小爱音箱在小米汽车发布会上同步瘫痪
2025 南京大学《操作系统原理》
数据中心里的并发编程

The C10K Problem

Dan Kegel, 1999

It's time for web servers to handle ten thousand clients simultaneously, don't you think? After all, the web is a big place now.

while (true) {
    Request *rq = get_request();
    pthread_create(&tid, NULL, handle_request, rq);
}
  • 10K 个请求,每个请求需要 1ms 处理
    • 需要 10s 才能处理完
    • 这简直就是 fork bomb!
2025 南京大学《操作系统原理》
数据中心里的并发编程

今天的版本答案

Serverless Computing: 我才不管并发呢

  • Function as a Service (FaaS): 系统自动 scale 到 C10M
    • 大号的 JavaScript 程序 (但没有 shared memory)
    • 需要 idempotence
const AWS = require('aws-sdk');
const dynamo = new AWS.DynamoDB.DocumentClient();

exports.handler = async (event) => {
    const body = JSON.parse(event.body);
    const { username, password } = body;

    await dynamo.put({
        TableName: 'Users',
        Item: { username, password, }
    }).promise();
    return { statusCode: 201, body: JSON.stringify(...), };
};
2025 南京大学《操作系统原理》
数据中心里的并发编程

用户是爽了,系统谁来实现?

Challenge: 高可靠、低延迟的多副本分布式存储和计算

  • 数据保持一致 (Consistency)、服务时刻可用 (Availability)、容忍机器离线 (Partition tolerance) 不可兼得

center

2025 南京大学《操作系统原理》
数据中心里的并发编程

协程:操作系统 “不感知” 的上下文切换

和线程概念相同

  • 进程内独立堆栈、共享内存的状态机
  • 但 “一直执行”,直到 yield() 主动放弃处理器
    • yield() 是函数调用
      • 只需保存/恢复 non-volatile 的寄存器
      • (线程切换需要保存/恢复全部寄存器)
    • 但 sleep (I/O) 时,所有协程都 “卡住了”
      • 失去了并行
2025 南京大学《操作系统原理》
数据中心里的并发编程

协程:例子

import random

def T_worker(name):
    i = 0
    while (i := i + 1):
        print(f'[{name}] i = {i}')
        yield()

threads = [T_worker(i) for i in range(1000000)]
while True:
    random.choice(threads).send(None)
  • 在同一个操作系统线程中执行
    • 可以由程序控制调度
    • 除了内存,不占用额外操作系统资源
2025 南京大学《操作系统原理》
数据中心里的并发编程

Go 和 Goroutine

小孩子才做选择,多处理器并行和轻量级并发我全都要!

  • Goroutine: 概念上是线程,实现是线程和协程的混合体

Goroutine 实现

  • 每个 CPU 上有一个 Go Worker Thread (协程调度器)
  • 协程执行 blocking API (sleep, read)
    • 偷偷调用 non-blocking 的版本
    • 成功 → 立即继续执行
    • 失败 → 立即 yield 到另一个需要 CPU 的 goroutine
      • 完全可以在单个服务器上启动 1,000,000 个 goroutine 共享处理器算力
2025 南京大学《操作系统原理》
数据中心里的并发编程

Go 语言中的同步与通信

Do not communicate by sharing memory; instead, share memory by communicating. ——Effective Go

共享内存 = 万恶之源

  • 信号量/条件变量:实现了同步,但没有实现 “通信”
    • 数据传递完全靠手工 (没上锁就错了)

但 UNIX 时代就有一个实现并行的机制了

  • cat *.txt | wc -l
    • 管道是一个天然的生产者/消费者!
    • 为什么不用 “管道” 实现协程/线程间的同步 + 通信呢?
      • Channels in Go
2025 南京大学《操作系统原理》