Go语言-->Goroutine 详细解释

Goroutine 详细解释

goroutine 是 Go 语言中的轻量级线程,由 Go 运行时管理。它是实现并发编程的核心机制。

核心概念

特性 说明
轻量级 创建成本极低,一个程序可以轻松创建数百万个 goroutine
并发执行 多个 goroutine 可以同时运行
由运行时管理 Go 运行时自动调度 goroutine 到 CPU 核心上
非抢占式 goroutine 只在特定点(如 I/O、channel 操作)让出控制权

基本用法

1. 创建 Goroutine

go 复制代码
package main

import (
	"fmt"
	"time"
)

func main() {
	// 普通函数调用(同步)
	sayHello("Alice")
	
	// 启动 goroutine(异步)
	go sayHello("Bob")
	
	// 主 goroutine 需要等待子 goroutine 完成
	time.Sleep(1 * time.Second)
}

func sayHello(name string) {
	fmt.Printf("Hello, %s\n", name)
}

输出:

复制代码
Hello, Alice
Hello, Bob

2. 多个 Goroutine

go 复制代码
package main

import (
	"fmt"
	"time"
)

func main() {
	for i := 1; i <= 5; i++ {
		go func(id int) {
			fmt.Printf("Goroutine %d 开始\n", id)
			time.Sleep(time.Duration(id) * time.Second)
			fmt.Printf("Goroutine %d 完成\n", id)
		}(i)
	}
	
	// 等待所有 goroutine 完成
	time.Sleep(6 * time.Second)
	fmt.Println("主程序结束")
}

输出:

复制代码
Goroutine 1 开始
Goroutine 2 开始
Goroutine 3 开始
Goroutine 4 开始
Goroutine 5 开始
Goroutine 1 完成
Goroutine 2 完成
Goroutine 3 完成
Goroutine 4 完成
Goroutine 5 完成
主程序结束

go func() 和 go sayHello("Bob") 的区别

特性 go func() go sayHello("Bob")
类型 匿名函数 命名函数
定义位置 调用处定义 提前定义
代码量 适合简短逻辑 适合复杂逻辑
可重用性 不可重用 可重用
闭包 可访问外部变量 需要参数传递

Goroutine vs 线程

特性 Goroutine 线程
内存占用 ~2KB ~1-2MB
创建速度 极快 较慢
数量 可创建百万级 通常数百个
切换成本 低 高
管理 Go 运行时 操作系统

与 Channel 结合

go 复制代码
package main

import (
	"fmt"
)

func main() {
	// 创建 channel
	results := make(chan string)
	
	// 启动 goroutine
	go func() {
		results <- "任务 1 完成"
	}()
	
	go func() {
		results <- "任务 2 完成"
	}()
	
	// 接收结果
	fmt.Println(<-results)
	fmt.Println(<-results)
}

输出:

复制代码
任务 1 完成
任务 2 完成

常见模式

1. 使用 sync.WaitGroup 等待完成

go 复制代码
package main

import (
	"fmt"
	"sync"
)

func main() {
	var wg sync.WaitGroup
	
	for i := 1; i <= 3; i++ {
		wg.Add(1)
		go func(id int) {
			defer wg.Done()
			fmt.Printf("Goroutine %d 执行\n", id)
		}(i)
	}
	
	wg.Wait()
	fmt.Println("所有 goroutine 完成")
}

2. 并发 HTTP 请求

go 复制代码
package main

import (
	"fmt"
	"net/http"
	"sync"
)

func main() {
	urls := []string{
		"https://example.com",
		"https://google.com",
		"https://github.com",
	}
	
	var wg sync.WaitGroup
	for _, url := range urls {
		wg.Add(1)
		go func(u string) {
			defer wg.Done()
			resp, err := http.Get(u)
			if err != nil {
				fmt.Printf("请求 %s 失败: %v\n", u, err)
			} else {
				fmt.Printf("请求 %s 成功,状态码: %d\n", u, resp.StatusCode)
			}
		}(url)
	}
	
	wg.Wait()
}

注意事项

⚠️ 常见问题:

  • 主 goroutine 过早退出:子 goroutine 还未完成就结束了
  • 竞态条件:多个 goroutine 同时修改共享变量
  • 死锁:goroutine 互相等待

✅ 最佳实践:

  • 使用 sync.WaitGroup 或 channel 等待 goroutine 完成
  • 使用 sync.Mutex 保护共享数据
  • 避免在 goroutine 中直接修改外部变量(使用参数传递)
相关推荐
1024奇点2 小时前
【仓颉语言入门 · 第二篇 第 22 课】
开发语言·ide·开源
实名上网宋凯宣2 小时前
Django
后端·python·django
Warren2Lynch2 小时前
C4 模型详解:利用 Visual Paradigm 可视化软件架构的综合指南
开发语言·uml
Knight_AL3 小时前
Java 文件下载时文件名中的 `#` 被转换成 `%23`?一次 URL 编码与解码问题排查
java·开发语言
利威尔xu3 小时前
Go IM 源码实战(14):本地缓存快 3000 倍,失效却难 10 倍
后端·缓存·微服务·面试·golang
FRing034 小时前
vue2干货总结2(v-bind、v-for、v-model、指令修饰符、v-bind操作class和style)
开发语言·前端·vue.js·vue2
逃逸线LOF4 小时前
Ribbon负载均衡原理(Irule组件、源码分析)
后端·spring cloud·ribbon
by209994 小时前
C++ 智能指针的所有权与使用
开发语言·c++·经验分享
砚底藏山河4 小时前
python量化自定义周期 K 线合成
java·开发语言·数据库·python·金融
慢云智慧空间4 小时前
从设备智能到空间理解,空间智能终端解决方案商如何重新定义行业标准
后端