Go语言Goroutine

在本教程中,我们将讨论如何使用 Goroutines 在 Go 中实现并发。

什么是 Goroutine?

Goroutine 是与其他函数或方法同时运行的函数或方法。Goroutines 可以被认为是轻量级线程。与线程相比,创建 Goroutine 的成本很小。因此,Go 应用程序同时运行数千个 Goroutine 是很常见的。

Goroutines 相对于线程的优点

  • 与线程相比,Goroutines 非常便宜。它们的堆栈大小只有几 kb,并且堆栈可以根据应用程序的需要增长和缩小,而在线程的情况下,堆栈大小必须指定并且是固定的。
  • Goroutines 被复用到较少数量的操作系统线程。一个具有数千个 Goroutine 的程序中可能只有一个线程。如果该线程中的任何 Goroutine 阻塞等待用户输入,则会创建另一个操作系统线程,并将剩余的 Goroutine 移动到新的操作系统线程。所有这些都由运行时处理,我们作为程序员从这些复杂的细节中抽象出来,并获得一个干净的 API 来处理并发。
  • Goroutine 使用通道进行通信。通道的设计可防止使用 Goroutines 访问共享内存时发生竞争条件。通道可以被认为是 Goroutine 用来进行通信的管道。我们将在下一个教程中详细讨论通道。

如何启动 Goroutine?

在函数或方法调用前加上关键字前缀go,您将有一个新的 Goroutine 同时运行。

让我们创建一个 Goroutine

package main

import (  
    "fmt"
)

func hello() {  
    fmt.Println("Hello world goroutine")
}
func main() {  
    go hello()
    fmt.Println("main function")
}

Run program in playground

在11行号中。go hello()启动一个新的Goroutine。现在该hello()函数将与该main()函数同时运行。main 函数在自己的 Goroutine 中运行,称为main Goroutine

运行这个程序你会有惊喜!

该程序仅输出文本main function。我们启动的 Goroutine 发生了什么?我们需要了解 goroutine 的两个主要属性才能理解为什么会发生这种情况。

  • 当一个新的 Goroutine 启动时,goroutine 调用立即返回。与函数不同,控件不会等待 Goroutine 完成执行。Goroutine 调用后,控件立即返回到下一行代码,并且 Goroutine 的任何返回值都将被忽略。
  • 主 Goroutine 应运行,以便其他 Goroutine 运行。如果主 Goroutine 终止,则程序将终止,并且不会运行其他 Goroutine。

我想现在你应该能够理解为什么我们的 Goroutine 没有运行了。

现在让我们解决这个问题。

package main

import (  
    "fmt"
    "time"
)

func hello() {  
    fmt.Println("Hello world goroutine")
}
func main() {  
    go hello()
    time.Sleep(1 * time.Second)
    fmt.Println("main function")
}

Run program in playground

在上面程序的第 13 行中,我们调用了time包的Sleep方法,该方法使正在执行的 go 协程休眠。在这种情况下,主 goroutine 会休眠 1 秒。现在,在主 Goroutine 终止之前,对 go hello()的调用有足够的时间执行。该程序首先打印,``Hello world goroutine`

这种在主 Goroutine 中使用 sleep 来等待其他 Goroutine 完成执行的方式是我们用来理解 Goroutines 如何工作的一种 hack。Channels 可用于阻塞主 Goroutine,直到所有其他 Goroutine 完成执行。我们将在下一个教程中讨论通道。

启动多个 Goroutine

让我们再编写一个启动多个 Goroutine 的程序,以更好地理解 Goroutine。

package main

import (  
    "fmt"
    "time"
)

func numbers() {  
    for i := 1; i <= 5; i++ {
        time.Sleep(250 * time.Millisecond)
        fmt.Printf("%d ", i)
    }
}
func alphabets() {  
    for i := 'a'; i <= 'e'; i++ {
        time.Sleep(400 * time.Millisecond)
        fmt.Printf("%c ", i)
    }
}
func main() {  
    go numbers()
    go alphabets()
    time.Sleep(3000 * time.Millisecond)
    fmt.Println("main terminated")
}

Run program in playground

上面的程序启动了两个 Goroutine。这两个 Goroutine 现在同时运行。Goroutinenumbers最初休眠 250 毫秒,然后打印1,然后再次休眠并打印2,同样的循环发生,直到打印 5。类似地,alphabetsGoroutine 打印字母从ae并有 400 毫秒的休眠时间。主 Goroutine 启动numbersalphabetsGoroutines,休眠 3000 毫秒,然后终止。

该程序输出

1 a 2 3 b 4 c 5 d e main terminated  

下图描述了该程序的工作原理。请在新选项卡中打开图像以获得更好的可见性:)

img

蓝色图像的第一部分代表数字Goroutine,栗色第二部分代表字母 Goroutine,绿色第三部分代表主 Goroutine,最后黑色部分融合了上述三个部分,并向我们展示了如何程序有效。每个框顶部的0 ms、250 ms 等字符串表示以毫秒为单位的时间,输出在每个框的底部表示为1、2、3等。蓝色框告诉我们 是在 1打印之后 的250 ms,之后打印的2,依此类推。最后一个黑框的底部有值,1 a 2 3 b 4 c 5 d e main terminated这也是程序的输出。该图像是不言自明的,您将能够理解该程序是如何工作的。

Goroutines 就这样了。祝你有美好的一天。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值