AI Library · 知识库

# CPU 并行:计算密集型任务与运行时限制

学习目标:理解 CPU 计算密集型任务与 I/O 密集型任务的本质差异,掌握多核真并行在进程、线程与运行时约束(如 Python GIL、Node.js 事件循环)下的工程选择。

前置知识与范围:多线程、多进程与系统调度。本篇聚焦通过真多核并行加速独立 CPU 密集型任务。

# 问题与边界

设计一个纯 CPU 密集型计算任务:

# 关键差异与运行时限制

语言 CPU 并行核心机制 核心运行时限制 为什么纯多线程在某些语言无效?
Go Goroutine 由 GMP 调度器自动分派至多 OS 线程(GOMAXPROCS) 协程上下文轻量,无锁运行 原生真多核并行
Rust std::thread::spawn 操作系统原生线程 强类型所有权(Send / Sync)编译期拦截数据竞争 原生真多核并行,零运行时损耗
Java ForkJoinPool / 线程池 操作系统线程映射 原生真多核并行
Python 必须使用 multiprocessing 进程池 **GIL(全局解释器锁)**限制同一时刻仅一个线程执行 Python 字节码 纯 threading.Thread 在 CPU 密集场景下不仅无法加速,甚至因频繁锁切换更慢!
JavaScript / Node 必须使用 worker_threads 主线程为单线程事件循环(Event Loop) 在主线程中执行繁重循环会直接彻底冻结整个事件循环,阻塞一切网络与定时器

# 各语言实现

# Go

Gopackage main

import (
	"fmt"
	"sync"
)

func sumRange(start, end int64) int64 {
	var s int64
	for i := start; i <= end; i++ {
		s += i
	}
	return s
}

func main() {
	var wg sync.WaitGroup
	var part1, part2 int64

	wg.Add(2)
	// 分派至两个并发协程,GMP 调度器在多核 CPU 上真并行执行
	go func() {
		defer wg.Done()
		part1 = sumRange(1, 1000000)
	}()
	go func() {
		defer wg.Done()
		part2 = sumRange(1000001, 2000000)
	}()

	wg.Wait()
	total := part1 + part2

	// 等差数列公式验证:2000000 * 2000001 / 2 = 2000001000000
	if total != 2000001000000 {
		panic("sum calculation mismatch")
	}

	fmt.Printf("total_sum: %d\n", total)
}

Go 运行时自动检测 CPU 核心数,将相互独立的计算任务透明派发到物理多核上运行。

# Rust

Rustuse std::thread;

fn sum_range(start: i64, end: i64) -> i64 {
    (start..=end).sum()
}

fn main() {
    // 启动原生操作系统线程
    let handle1 = thread::spawn(|| sum_range(1, 1_000_000));
    let handle2 = thread::spawn(|| sum_range(1_000_001, 2_000_000));

    let part1 = handle1.join().unwrap();
    let part2 = handle2.join().unwrap();
    let total = part1 + part2;

    assert_eq!(total, 2_000_001_000_000);

    println!("total_sum: {total}");
}

Rust 的 thread::spawn 创建的是真正的 1:1 操作系统线程,没有 GIL,没有中间层锁竞争。

# Java

Javaimport java.util.concurrent.CompletableFuture;

public class Main {
    public static long sumRange(long start, long end) {
        long s = 0;
        for (long i = start; i <= end; i++) {
            s += i;
        }
        return s;
    }

    public static void main(String[] args) {
        // 利用通用 ForkJoinPool 并行执行
        CompletableFuture<Long> task1 = CompletableFuture.supplyAsync(() -> sumRange(1, 1_000_000));
        CompletableFuture<Long> task2 = CompletableFuture.supplyAsync(() -> sumRange(1_000_001, 2_000_000));

        long total = task1.join() + task2.join();

        assert total == 2_000_001_000_000L;
        System.out.println("total_sum: " + total);
    }
}

Java 强大的并发线程池体系使 CPU 密集型任务的并行化变得高度结构化。

# Python(多进程破解 GIL)

Pythonfrom multiprocessing import Pool

def sum_range(bounds: tuple) -> int:
    start, end = bounds
    s = 0
    for i in range(start, end + 1):
        s += i
    return s

if __name__ == "__main__":
    # 必须在 if __name__ == '__main__' 守护下启动多进程(防止 Windows 进程派生无限递归)
    with Pool(processes=2) as pool:
        results = pool.map(sum_range, [(1, 1000000), (1000001, 2000000)])

    total = sum(results)
    assert total == 2000001000000

    print(f"total_sum: {total}")

在 CPython 中,每个进程拥有独立的 Python 解释器和独立的 GIL,因此能够真正同时跑满多核 CPU。

# JavaScript(工作线程)

在 Node.js 中,主线程负责处理事件、定时器与网络 I/O。对于繁重的 CPU 计算,官方提供标准库 node:worker_threads 启动具有独立 V8 实例与事件循环的隔离工作线程。

JavaScriptconst assert = {
  equal: (a, b) => { if (a !== b) throw new Error(`${a} !== ${b}`); },
  deepEqual: (a, b) => { if (JSON.stringify(a) !== JSON.stringify(b)) throw new Error(`${JSON.stringify(a)} !== ${JSON.stringify(b)}`); },
  throws: (fn, errType) => {
    try { fn(); throw new Error("expected throw"); } catch (e) {
      if (e.message === "expected throw") throw e;
      if (errType && !(e instanceof errType)) throw e;
    }
  }
};
import { Worker, isMainThread, parentPort, workerData } from "node:worker_threads";
import { fileURLToPath } from "node:url";

const __filename = fileURLToPath(import.meta.url);

if (isMainThread) {
  function runWorker(start, end) {
    return new Promise((resolve, reject) => {
      const worker = new Worker(__filename, { workerData: { start, end } });
      worker.on("message", resolve);
      worker.on("error", reject);
    });
  }

  const [part1, part2] = await Promise.all([
    runWorker(1, 1000000),
    runWorker(1000001, 2000000),
  ]);

  const total = part1 + part2;
  assert.equal(total, 2000001000000);
  console.log(`total_sum: ${total}`);
} else {
  // 子工作线程:独立 CPU 密集执行
  const { start, end } = workerData;
  let s = 0;
  for (let i = start; i <= end; i++) {
    s += i;
  }
  parentPort.postMessage(s);
}

通过 worker_threads,计算被安全隔离在子线程,主线程事件循环不被阻塞。

# 迁移误区与适用限制

# 运行与验证

语言版本基线见示例基线。 所有程序运行后控制台输出均为:

纯文本total_sum: 2000001000000

多核并行计算、等差数列数学校验与主进程收束测试全部通过。

# 相关阅读