Goroutine是Go语言实现并发的核心机制,是一种轻量级的线程。与传统操作系统线程相比,goroutine的创建和销毁成本极低,一个Go程序可以轻松创建成千上万个goroutine。
使用go关键字即可启动一个新的goroutine。
package main
import (
"fmt"
"time"
)
func sayHello() {
fmt.Println("Hello from goroutine!")
}
func main() {
// 启动一个goroutine
go sayHello()
// 主goroutine等待,否则程序会立即退出
time.Sleep(time.Second)
fmt.Println("Main function")
}
package main
import (
"fmt"
"time"
)
func main() {
// 使用匿名函数启动goroutine
go func() {
fmt.Println("匿名goroutine执行")
}()
// 带参数的匿名goroutine
go func(msg string) {
fmt.Println(msg)
}("Hello, Goroutine!")
time.Sleep(time.Second)
}
Go运行时使用GMP模型来调度goroutine:
package main
import (
"fmt"
"runtime"
)
func main() {
// 获取当前GOMAXPROCS值
fmt.Println("GOMAXPROCS:", runtime.GOMAXPROCS(0))
// 设置使用的CPU核心数
runtime.GOMAXPROCS(4)
// 获取CPU核心数
fmt.Println("CPU核心数:", runtime.NumCPU())
// 获取当前goroutine数量
fmt.Println("Goroutine数量:", runtime.NumGoroutine())
}
sync.WaitGroup用于等待一组goroutine完成。
package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done() // 完成时调用Done()
fmt.Printf("Worker %d 开始工作\n", id)
time.Sleep(time.Second)
fmt.Printf("Worker %d 完成工作\n", id)
}
func main() {
var wg sync.WaitGroup
// 启动5个worker
for i := 1; i <= 5; i++ {
wg.Add(1) // 增加计数
go worker(i, &wg)
}
// 等待所有goroutine完成
wg.Wait()
fmt.Println("所有worker完成")
}
package main
import "fmt"
func worker(done chan bool) {
fmt.Println("工作中...")
// 模拟工作
fmt.Println("工作完成")
done <- true // 发送完成信号
}
func main() {
done := make(chan bool)
go worker(done)
<-done // 等待完成信号
fmt.Println("主程序退出")
}
Go语言推荐使用channel进行goroutine间通信,而不是共享内存。
"不要通过共享内存来通信,而应该通过通信来共享内存"
Don't communicate by sharing memory; share memory by communicating.
package main
import (
"fmt"
"sync"
)
type Counter struct {
mu sync.Mutex
value int
}
func (c *Counter) Increment() {
c.mu.Lock()
defer c.mu.Unlock()
c.value++
}
func (c *Counter) Value() int {
c.mu.Lock()
defer c.mu.Unlock()
return c.value
}
func main() {
var wg sync.WaitGroup
counter := &Counter{}
// 启动100个goroutine,每个增加计数器1000次
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for j := 0; j < 1000; j++ {
counter.Increment()
}
}()
}
wg.Wait()
fmt.Println("最终计数:", counter.Value()) // 100000
}
Goroutine泄漏是指goroutine无法正常退出,导致资源无法释放。
// 错误示例:goroutine永远阻塞
func leak() {
ch := make(chan int)
go func() {
val := <-ch // 永远等待,因为没有发送者
fmt.Println(val)
}()
// goroutine泄漏!
}
// 正确示例:使用context控制生命周期
func noLeak(ctx context.Context) {
ch := make(chan int)
go func() {
select {
case val := <-ch:
fmt.Println(val)
case <-ctx.Done():
return // 正常退出
}
}()
}
package main
import (
"fmt"
"sync"
"time"
)
func download(url string, wg *sync.WaitGroup) {
defer wg.Done()
fmt.Printf("开始下载: %s\n", url)
time.Sleep(time.Second * 2) // 模拟下载
fmt.Printf("完成下载: %s\n", url)
}
func main() {
urls := []string{
"https://example.com/file1.zip",
"https://example.com/file2.zip",
"https://example.com/file3.zip",
"https://example.com/file4.zip",
}
var wg sync.WaitGroup
for _, url := range urls {
wg.Add(1)
go download(url, &wg)
}
wg.Wait()
fmt.Println("所有文件下载完成")
}
package main
import (
"fmt"
"sync"
)
func sum(numbers []int, result chan int) {
sum := 0
for _, num := range numbers {
sum += num
}
result <- sum
}
func main() {
numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
// 分成两部分并发计算
mid := len(numbers) / 2
result := make(chan int, 2)
go sum(numbers[:mid], result)
go sum(numbers[mid:], result)
// 收集结果
sum1, sum2 := <-result, <-result
total := sum1 + sum2
fmt.Println("总和:", total) // 55
}
package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, jobs <-chan int, results chan<- int, wg *sync.WaitGroup) {
defer wg.Done()
for job := range jobs {
fmt.Printf("Worker %d 处理任务 %d\n", id, job)
time.Sleep(time.Millisecond * 500)
results <- job * 2
}
}
func main() {
const numJobs = 10
const numWorkers = 3
jobs := make(chan int, numJobs)
results := make(chan int, numJobs)
var wg sync.WaitGroup
// 启动worker池
for w := 1; w <= numWorkers; w++ {
wg.Add(1)
go worker(w, jobs, results, &wg)
}
// 发送任务
for j := 1; j <= numJobs; j++ {
jobs <- j
}
close(jobs)
// 等待所有worker完成
go func() {
wg.Wait()
close(results)
}()
// 收集结果
for result := range results {
fmt.Println("结果:", result)
}
}
| 项目 | Goroutine | 系统线程 |
|---|---|---|
| 初始栈大小 | 2KB | 1-2MB |
| 创建时间 | ~微秒级 | ~毫秒级 |
| 上下文切换 | ~纳秒级 | ~微秒级 |
| 最大数量 | 数十万+ | 数千 |