Logo
Section 1 / 3

关于Goroutine的一切

了解 Golang 中的 goroutine。

导入

关于Goroutine的一切

了解 Golang 中的 goroutine。

goroutine 的作用是什么?

假设我们需要发起一个 GET 请求,然后处理响应。在同一段代码中,还存在其他一些与 HTTP 请求无关、也不依赖响应结果的操作。由于代码是顺序执行的,我们会先遇到 GET 请求。

一旦发送请求,就必须等待服务器返回响应。在此期间,程序会阻塞,即使后续操作与该请求毫无关系也无法继续执行。这种效率低下的情况很常见,而我们通常通过多线程和并行处理来解决这一问题。

Go 语言以一种简洁且易于理解的方式解决了这个问题:只需将相关代码放入独立的 goroutine 中。多个 goroutine 可以彼此并行执行。使用 goroutine 后,GET 请求将在当前函数之外独立运行,而那些不涉及 HTTP 请求的代码也可以不受影响地并发执行。

下面我们先来看一段没有使用 goroutine 的代码示例:

1package main 2 3import ( 4 "fmt" 5 "time" 6) 7 8func main() { 9 start := time.Now() 10 a, b := 4, 5 11 fmt.Printf("The product of %d and %d is %d\n",a,b, (a * b)) 12 time.Sleep(2*time.Second) 13 fmt.Printf("The sum of %d and %d is %d\n",a,b,(a + b)) 14 time.Sleep(2*time.Second) 15 fmt.Printf("%s", time.Since(start)) 16}

上述代码的总运行时间约为四秒。然而我们知道,乘法运算和加法运算彼此完全独立,可以同时进行。但由于代码是顺序执行的,每次 time.Sleep 都会累加到总耗时中,导致整体执行时间翻倍。

现在我们改用 goroutine 来查看执行时间的变化:

1package main 2 3import ( 4 "fmt" 5 "sync" 6 "time" 7) 8 9var wg sync.WaitGroup 10 11func main() { 12 start := time.Now() 13 a, b := 4, 5 14 wg.Add(2) 15 go func() { 16 fmt.Printf("The multiplication of %d and %d is %d\n",a,b, (a * b)) 17 time.Sleep(2*time.Second) 18 wg.Done() 19 }() 20 go func() { 21 fmt.Printf("The sum of %d and %d is %d\n",a,b,(a + b)) 22 time.Sleep(2*time.Second) 23 wg.Done() 24 }() 25 26 wg.Wait() 27 fmt.Printf("%s", time.Since(start)) 28}

此时,程序的运行时间仅约两秒,因为两个函数在各自的 goroutine 中并行执行。尽管 time.Sleep 被调用了两次,但它们分别运行在不同的 goroutine 中,互不影响。

线程与 goroutine 的区别

创建子线程或进行线程分叉是一项开销较大的操作,涉及线程创建、内存分配、上下文切换等多个系统级操作。

以下是线程与 goroutine 的主要区别对比:

线程goroutine
固定栈大小(通常 ≥ 1MB)栈大小可动态增长和收缩
线程间通信较为复杂goroutine 通过 channel 进行通信
创建和销毁需要较多系统资源创建和销毁开销极小
大量创建线程代价高昂可轻松创建成千上万个 goroutine
上下文切换开销大上下文切换轻量,需保存的状态更少

如何创建一个 goroutine

创建 goroutine 非常简单:只需将代码封装在函数中,并在调用前加上 go 关键字即可。例如,我们可以为一个执行乘法运算并打印结果的函数创建 goroutine,也可以为匿名函数创建 goroutine。

示例如下:

1package main 2 3import ( 4 "fmt" 5 "sync" 6) 7 8var wg sync.WaitGroup 9 10func multiplication(a, b int) { 11 fmt.Printf("The muliplication of %d and %d is %d\n", a, b, a*b) 12 wg.Done() 13} 14 15func main() { 16 a, b := 2, 3 17 wg.Add(2) 18 // 使用 go 关键字启动 multiplication 函数作为独立的 goroutine 19 go multiplication(a, b) 20 21 go func(){ 22 fmt.Printf("The subtraction of %d from %d is %d\n", a, b, b-a) 23 wg.Done() 24 }() 25 26 wg.Wait() 27}

同步

匿名Goroutine