go 并发 并行 异步

10 阅读3分钟

在 Go 语言中,理解 并发 (Concurrency)并行 (Parallelism)异步 (Asynchrony) 的区别是进阶的关键。Go 的设计哲学深受这些概念的影响。

1. 核心概念对比 (Analogy)

我们可以用“咖啡馆”来做类比:

概念类比场景关注点
并发 (Concurrency)一个服务员同时为多桌客人服务。他在 A 桌点完菜,不等菜上桌,就去 B 桌倒水。他是在处理多件事,但某一瞬间只能做一件事。结构 (Structure):如何组织代码以处理多个任务。
并行 (Parallelism)多个服务员同时工作。服务员 A 在给 A 桌点菜,服务员 B 同时在给 B 桌倒水。他们在执行多件事。执行 (Execution):在多核 CPU 上同时运行。
异步 (Asynchrony)客人点完餐后拿到一个取餐号,然后回座位玩手机。等厨房做好了,会通过广播(回调/信号)通知他。非阻塞 (Non-blocking):发起请求后立即返回,不原地等待结果。

2. Go 语言中的实现

A. 并发 (Concurrency) —— Go 的强项

Go 通过 Goroutine (协程) 实现并发。Go 的口号是:“不要通过共享内存来通信,而要通过通信来共享内存。” 并发是 Go 程序的设计属性。即使在单核 CPU 上,你也可以开启 100 万个协程。

B. 并行 (Parallelism) —— 硬件支撑

Go 运行时(Runtime)会自动将并发的协程调度到多个系统线程上。如果你的电脑有多个 CPU 核心,Go 就会自动实现并行。 你可以通过 runtime.GOMAXPROCS(n) 来限制并行使用的核心数。

C. 异步 (Asynchrony) —— “伪同步”写法

在 Node.js 中,异步通常通过 callback, Promise, async/await 实现。 在 Go 中,异步逻辑是用同步的方式写的。当你发起一个网络请求时,当前的协程会“阻塞”,但 Go 运行时的底层其实是异步非阻塞的(使用 epoll/kqueue),它会自动把 CPU 让给其他协程。


3. 代码演示与详解

package main

import (
	"fmt"
	"runtime"
	"sync"
	"time"
)

func task(name string, wg *sync.WaitGroup) {
	defer wg.Done()
	for i := 1; i <= 3; i++ {
		fmt.Printf("任务 %s: 正在处理第 %d 步\n", name, i)
		// 模拟耗时操作(这里会触发协程切换,体现并发)
		time.Sleep(time.Millisecond * 100)
	}
}

func main() {
	// 1. 并行度设置:查看当前系统的 CPU 核心数
	cpuCores := runtime.NumCPU()
	fmt.Printf("系统核心数: %d\n", cpuCores)
    
	// 2. 这里的 WaitGroup 用于同步等待所有协程完成(类似于 Promise.all)
	var wg sync.WaitGroup

	fmt.Println("--- 程序开始运行 ---")

	// 3. 启动两个并发任务
	wg.Add(2)
	go task("A", &wg) // 开启协程 A
	go task("B", &wg) // 开启协程 B

	// 这里的代码继续执行,体现了“异步”发起的特性
	fmt.Println("主线程:我已经下达了任务,现在我去忙别的了...")

	wg.Wait() // 阻塞等待 A 和 B 完成
	fmt.Println("--- 所有任务完成 ---")
}

4. 深度对比:Go vs Node.js

特性Go 语言Node.js (JavaScript)
模型CSP (通信顺序进程)Event Loop (事件循环)
线程多线程 (M:N 调度)单线程 (通过异步 I/O 模拟并发)
阻塞感看起来是同步阻塞的,实则异步。代码顺序执行,逻辑清晰。必须使用 await 或回调,否则会产生异步副作用。
复杂任务擅长 CPU 密集型 + I/O 密集型。擅长 I/O 密集型,CPU 密集型会阻塞事件循环。

总结:如何理解?

  1. 并发是逻辑上的:你在代码里写了 go func(),你的程序就具备了并发处理的能力(结构)。
  2. 并行是物理上的:当你的程序运行在多核机器上,Go 自动让这些 go func() 在不同的核心上同时跑(效率)。
  3. 异步是体验上的:在 Go 里,你不需要写复杂的 thencallback。Go 让你用最简单的同步代码,享受高性能的异步底层。

一句话:Go 的伟大之处在于,它用并发(协程)的简单模型,完美利用了并行的硬件能力,并屏蔽了异步编程的复杂性。