Goroutine协程

Goroutine 是 Go 语言的并发编程模型,它是一种轻量级的线程,由 Go 运行时管理,我们也可以称之为协程。

优点:

  • 轻量级:Goroutine 的栈空间初始大小只有 2KB,可以动态扩容,最大可达 1GB

  • 快速启动:Goroutine 的启动时间只有 1~2us

  • 高效调度:Goroutine 的调度器采用 M:N 模型,可以将 M 个 Goroutine 映射到 N 个 OS 线程上,实现高效调度

  • 通信简单:Goroutine 之间通过 Channel 进行通信,实现数据共享

  • 无锁:Goroutine 之间通过 Channel 进行通信,无需加锁

  • 高并发:Goroutine 可以轻松创建数十万个,实现高并发

  • 高性能:Goroutine 的调度器采用抢占式调度,实现高性能

使用 go 加上任意 func 即可创建一个 Goroutine,Goroutine 会在后台执行,不会阻塞主线程。

如何停止 Goroutine

  • 运行结束:Goroutine 会在函数运行结束后自动结束

  • 超时结束:通过 context.WithTimeout()context.WithDeadline() 可以设置 Goroutine 的超时时间

  • 手动结束:通过 context.WithCancel() 可以手动结束 Goroutine

  • 通道结束:通过 Channel 通信,可以结束 Goroutine

Goroutine 和 Channel

我们知道,无论是在线程还是协程,在运行的时候都会遇到贡献数据或传递数据的情况,在 Golang 中,我们可以通过 Channel 来实现 Goroutine 之间的通信。

package main

import (
    "fmt"
    "time"
)

func main() {
    ch := make(chan int)

    go func() {
        for {
            select {
            case <-ch:
                fmt.Println("exit")
                return
            default:
                fmt.Println("running...")
                time.Sleep(time.Second)
            }
        }
    }()

    time.Sleep(5 * time.Second)
    ch <- 1
}

在上面的例子中,我们创建了一个 Channel ch,在主线程中向 ch 中发送了一个数据,Goroutine 中通过 select 语句监听 ch,当 ch 中有数据时,Goroutine 会退出。

协程之间通过 Channel 通信的例子:

package main

import (
	"fmt"
	"time"
)

func main() {
	ch := make(chan string)

	go sendData(ch)
	go getData(ch)

	time.Sleep(time.Second)
}

func sendData(ch chan string) {
	ch <- "Bilibili"
	ch <- "Youtube"
}

func getData(ch chan string) {
	var input string
	for {
		input = <-ch
		fmt.Printf("%s ", input)
	}
}

// 结果: Bilibili Youtube

Goroutine 字段介绍

字段
说明

goid

Goroutine ID, 唯一标识符

status

Goroutine 状态, 如运行和阻塞

stack

Goroutine 栈空间

gopc

Goroutine PC 寄存器

m

Goroutine 所在的 M

locked

Goroutine 是否被锁定

sched

Goroutine 调度器

atomicstatus

Goroutine 原子状态

Goroutine 的9种状态类型

字段

编号

描述

_Gidle

0

表示此 Goroutine 刚刚分配并且尚未初始化。

_Grunnable

1

表示此 Goroutine 在运行队列中。它当前不执行用户代码。堆栈不属于它。

_Grunning

2

表示此 Goroutine 可能执行用户代码。堆栈由此 Goroutine 拥有。它不在运行队列中。它被分配给一个 M 和一个 P。

_Gsyscall

3

表示此 Goroutine 正在执行系统调用。它不执行用户代码。堆栈由此 Goroutine 拥有。它不在运行队列中。它被分配给一个 M。

_Gwaiting

4

表示此 Goroutine 在运行时被阻塞。它不执行用户代码。它不在运行队列中,但应该在某个地方记录(例如,一个通道等待队列),以便在必要时可以准备就绪。堆栈不属于它,除非在适当的通道锁下,通道操作可能读取或写入堆栈的某些部分。否则,在 Goroutine 进入 _Gwaiting 后访问堆栈是不安全的(例如,它可能会被移动)。

_Gmoribund_unused

5

目前未使用,但在 gdb 脚本中硬编码。

_Gdead

6

表示此 Goroutine 当前未使用。它可能刚刚退出,位于空闲列表上,或者刚刚初始化。它不执行用户代码。它可能具有堆栈,也可能没有。G 和其堆栈(如果有)由正在退出 G 的 M 或从空闲列表中获取 G 的 M 拥有。

_Genqueue_unused

7

目前未使用。

_Gcopystack

8

表示此 Goroutine 的堆栈正在移动。它不执行用户代码,也不在运行队列中。堆栈由将其放入 _Gcopystack 的 Goroutine 拥有。

_Gscan

0x1000

与除 _Grunning 之外的其他状态组合表示 GC 正在扫描堆栈。Goroutine 不执行用户代码,堆栈由设置 _Gscan 位的 Goroutine 拥有。

最后更新于