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.WaitGroupchannel 等待 goroutine 完成
  • 使用 sync.Mutex 保护共享数据
  • 避免在 goroutine 中直接修改外部变量(使用参数传递)
相关推荐
想吃火锅10059 小时前
【leetcode】405.数字转换为十六进制数js
开发语言·javascript·ecmascript
专注VB编程开发20年9 小时前
AI 生成C# WinForm 窗体 = 目前就是垃圾
开发语言·人工智能·c#
cfm_29149 小时前
JVM GC垃圾回收初步了解
java·开发语言·jvm
~小先生~9 小时前
Python从入门到放弃(一)
开发语言·python
许彰午10 小时前
17_synchronized关键字深度解析
java·开发语言
z落落10 小时前
C# 泛型接口和泛型类+泛型约束
开发语言·c#
阿正的梦工坊10 小时前
【Rust】02-变量、不可变性与基础类型
开发语言·后端·rust
阿正的梦工坊10 小时前
【Rust】08-集合类型、字符串与迭代器入门
开发语言·rust·c#
FuckPatience10 小时前
C# 使用泛型协变将派生类类型替换为基类类型
开发语言·c#
张忠琳10 小时前
【Go 1.26.4】(Part 1) Go 1.26.4 超深度源码分析 — 总体架构与模块全景
开发语言·golang