本项目用于对比和学习 Go 语言(Goroutine) 与 Python(Asyncio) 的协程并发机制。
通过一个模拟执行 3 个不同耗时任务(1秒、2秒、1秒)的经典场景,直观展示两者的编写方式、运行机制与执行效率。
目录结构
text
goroutine/
├── main.go # Go 语言协程实现(含 WaitGroup 与 Channel)
├── main.py # Python 协程实现(基于 asyncio 与 async/await)
└── README.md # 项目说明与编译运行指南
一、Go 语言:编译与运行指南
源码:
go
package main
import (
"fmt"
"sync"
"time"
)
// worker 模拟耗时任务
// 参数:id 任务编号,seconds 模拟耗时秒数,results 传递结果的 channel,wg 等待计数器
func worker(id int, seconds int, results chan<- string, wg *sync.WaitGroup) {
defer wg.Done() // 函数退出时通知 WaitGroup 任务已完成
fmt.Printf("[Go 协程 %d] 开始执行,预计耗时 %d 秒...\n", id, seconds)
time.Sleep(time.Duration(seconds) * time.Second) // 模拟 I/O 等待
// 将结果发送到 channel
results <- fmt.Sprintf("任务 %d 完成,实际耗时 %d 秒", id, seconds)
fmt.Printf("[Go 协程 %d] 执行完毕!\n", id)
}
func main() {
startTime := time.Now()
var wg sync.WaitGroup
// 缓冲区大小为 3 的 channel,避免发送方阻塞,协程之间用来传递 string 的通信管道
results := make(chan string, 3)
// 任务清单:[任务id, 模拟耗时(秒)]
tasks := [][2]int{
{1, 1},
{2, 2},
{3, 1},
}
fmt.Println("=== Go 协程(Goroutine)并发演示 ===")
// 并发启动任务
for _, task := range tasks {
wg.Add(1)
// 关键字 'go' 启动一个轻量级协程
go worker(task[0], task[1], results, &wg)
}
// 启动一个独立的 goroutine 等待所有任务完成,然后关闭 channel
go func() {
wg.Wait()
close(results)
}()
// 从 channel 读取并打印结果
for res := range results {
fmt.Printf("--> 收到结果: %s\n", res)
}
fmt.Printf("所有任务执行完毕,总耗时: %v\n", time.Since(startTime).Round(time.Millisecond))
}
1. 直接运行源码(开发调试)
在项目根目录下打开终端(PowerShell 或 CMD),直接运行:
bash
go run main.go
输出:
=== Go 协程(Goroutine)并发演示 ===
[Go 协程 1] 开始执行,预计耗时 1 秒...
[Go 协程 3] 开始执行,预计耗时 1 秒...
[Go 协程 2] 开始执行,预计耗时 2 秒...
[Go 协程 3] 执行完毕!
[Go 协程 1] 执行完毕!
--> 收到结果: 任务 3 完成,实际耗时 1 秒
--> 收到结果: 任务 1 完成,实际耗时 1 秒
[Go 协程 2] 执行完毕!
--> 收到结果: 任务 2 完成,实际耗时 2 秒
所有任务执行完毕,总耗时: 2.002s
2. 编译生成 .exe 可执行文件
方式 A:基础编译(默认命名)
在当前目录下直接执行 go build,会生成与目录同名或默认的 main.exe:
bash
go build main.go
执行后会在当前目录生成 main.exe。
方式 B:指定输出文件名(推荐)
使用 -o 参数指定输出的可执行文件名称,例如输出为 goroutine_demo.exe:
bash
go build -o goroutine_demo.exe main.go
方式 C:压缩体积编译(生产推荐)
如果希望减小 .exe 文件体积,可以加入 -ldflags="-s -w" 参数:
-s:去掉符号表信息。-w:去掉 DWARF 调试信息。
bash
go build -ldflags="-s -w" -o goroutine_demo.exe main.go
提示:该参数通常可使编译出的可执行文件体积缩减约 20%~30%,且运行功能完全一致。
方式 D:跨平台交叉编译(在 Linux / macOS 下构建 Windows exe)
Go 支持极其便捷的交叉编译,只需指定环境变量 GOOS 和 GOARCH:
-
Windows PowerShell 环境:
powershell$env:GOOS="windows" $env:GOARCH="amd64" go build -o goroutine_demo.exe main.go -
Linux / macOS Bash 环境:
bashCGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o goroutine_demo.exe main.go
3. 运行生成的 .exe 文件
在 Windows 终端中运行:
powershell
.\goroutine_demo.exe
二、Python 语言:运行指南
源码:
python
import asyncio
import sys
import time
# 兼容 Windows 控制台的中文输出
if sys.platform == "win32":
sys.stdout.reconfigure(encoding="utf-8")
# worker 异步协程函数
# 在 Python 中,使用 async def 定义协程
async def worker(task_id: int, seconds: int) -> str:
print(f"[Python 协程 {task_id}] 开始执行,预计耗时 {seconds} 秒...")
# asyncio.sleep 是非阻塞式的异步等待,不能用 time.sleep(time.sleep 会阻塞事件循环线程)
await asyncio.sleep(seconds)
print(f"[Python 协程 {task_id}] 执行完毕!")
return f"任务 {task_id} 完成,实际耗时 {seconds} 秒"
async def main():
start_time = time.perf_counter()
# 任务清单:(任务id, 模拟耗时秒数)
tasks = [
(1, 1),
(2, 2),
(3, 1),
]
print("=== Python 协程(Asyncio)并发演示 ===")
# 使用 asyncio.gather 并发调度多个协程,并等待它们全部完成
# 类似 Go 的 sync.WaitGroup + channel 收集返回结果
results = await asyncio.gather(
*(worker(task_id, delay) for task_id, delay in tasks)
)
# 打印收集到的结果
for res in results:
print(f"--> 收到结果: {res}")
total_time = time.perf_counter() - start_time
print(f"所有任务执行完毕,总耗时: {total_time:.3f} 秒")
if __name__ == "__main__":
# 启动 Python 的异步事件循环
asyncio.run(main())
确保系统已安装 Python 3.7+(本示例在 Python 3.14 下验证通过),直接在终端执行:
bash
python main.py
输出:
=== Python 协程(Asyncio)并发演示 ===
[Python 协程 1] 开始执行,预计耗时 1 秒...
[Python 协程 2] 开始执行,预计耗时 2 秒...
[Python 协程 3] 开始执行,预计耗时 1 秒...
[Python 协程 1] 执行完毕!
[Python 协程 3] 执行完毕!
[Python 协程 2] 执行完毕!
--> 收到结果: 任务 1 完成,实际耗时 1 秒
--> 收到结果: 任务 2 完成,实际耗时 2 秒
--> 收到结果: 任务 3 完成,实际耗时 1 秒
所有任务执行完毕,总耗时: 2.015 秒
三、Go vs Python 核心机制对比
| 对比维度 | Go 语言 (Goroutine) | Python (Asyncio) |
|---|---|---|
| 关键字/语法 | go func(...) |
async def / await |
| 底层模型 | GMP 调度模型(M:N 线程复用) | 单线程事件循环(Event Loop) |
| 多核利用 | 原生支持多核 CPU 并行调度 | 单进程单线程循环,受 GIL 限制;多核需结合多进程 |
| 调度特点 | 抢占式 + 协作式调度,普通阻塞自动切出 | 严格的协作式调度,必须在 await 处主动出让控制权 |
| 函数染色 | 无染色 :普通函数即可用 go 异步运行 |
有染色 :异步协程必须在 async 上下文中被 await |
| 通信机制 | 基于 CSP 模型的 Channel(通道)传递消息 | 函数返回值配合 asyncio.gather 或 asyncio.Queue |
| 分发与部署 | 静态编译为独立 exe,无依赖即可在目标机器运行 | 依赖目标机器已安装 Python 解释器及相关依赖环境 |