【Go实战基础】轻量级线程 goroutine

目录

一、简介

二、数据结构

1、G

2、P

3、M

三、菜鸟实战

1、创建 g008.go

2、编译和运行

3、运行结果

一、简介

goroutine 是 Go 里的一种轻量级线程,也叫做协程,Go 语言中,每一个并发的执行单元叫一个 goroutine,是一种轻量级线程。每个 goroutine 都有一个 sched 的属性用来保存它的上下文,在用户态下可以切换,切换的时候不用进入内核态,所以其切换代价非常小。

goroutine 作为 Go 语言的并发利器,不仅性能强劲而且使用方便,且 goroutine 占用内存极小,独立的并发任务比较简单,只需要用go 关键字修饰函数就可以启用一个 goroutine 直接运行,所以开发 go 程序的时候很多开发者常常会使用这个并发工具。

二、数据结构

G、P、M 三者是 Go 实现高并发的最为重要的概念,runtime 通过 调度器 来实现三者的相互调度执行,通过 p 将用户态的 g 与内核态资源 m 的动态绑定来执行,以减少以前通过频繁创建内核态线程而产生的一系列的性能问题,充分发挥服务器最大有限资源。

1、G

G 是 goroutine 的缩写,是运行时的最小执行单元


struct G
{
    stackguard   uintptr;      // 分段栈的可用空间下界
    stackbase    uintptr;      // 分段栈的栈基址
    sched        gobuf;        // 进程切换时,利用 sched 保存上下文
    stack        stack;        // 当前使用的栈空间,包括 [lo, hi]两个成员
	stackguard0  uintptr       // 用于检测是否需要进行栈扩张,go代码使用
	stackguard1  uintptr       // 用于检测是否需要进行栈扩展,原生代码使用的
    fnstart      FuncVal*;     // 运行的函数
    param        void*;        // 传递参数
    status       int16;        // 状态
    goid         int64;        // id 号
    m            m*;           // 当前 g 所绑定的m
	lockedm      muintptr      // g 是否要求要回到这个 M 执行
    gopc         uintptr;      // 创建这个 goroutine 的go表达式的 pc
    ...
};

2、P

P 指 Process, 是 M 运行 G 所需的资源,协程调度器的基本调度单元,G的容身之所,连接G和M的桥梁。

// process 结构
type p struct {
   lock        mutex     // 互斥锁
   id          int32     // id
   status      uint32    // p的状态,稍后介绍
   link        puintptr  // 下一个p的地址,可参考 g.schedlink
   m           muintptr  // p所关联的m
   mcache      *mcache   // 内存分配的时候用的,p所属的m的mcache用的也是这个
  
   goidcache    uint64   // 从sched中获取并缓存的id,避免每次分配goid都从sched分配
   goidcacheend uint64

   runqhead uint32       // p 本地的runnbale的goroutine形成的队列
   runqtail uint32
   runq     [256]guintptr

   runnext guintptr      // 下一个执行的g,如果是nil,则从队列中获取下一个执行的g

   gfree    *g           // 状态为 Gdead的g的列表,可以进行复用
   gfreecnt int32
}

3、M

M 指 machine,代表操作系统线程,也就是我们经常理解的线程,是真正参与操作系统调度的单元,每个 goroutine 只有依附于某个 M 方可真正地执行。

// machine 数据结构
type m struct {
   g0            *g          // g0 是用于调度和执行系统调用的特殊 g
   curg          *g          // m 当前运行的 g
   p             puintptr    // 当前拥有的 p
   tls           [6]uintptr  // 线程的 local storage
   nextp         puintptr    // 唤醒m时,m会拥有这个p
   id            int64       // id
   preemptoff    string      // 如果 !="", 继续运行curg
   spinning      bool        // 自旋状态,用于判断 m 是否工作已结束,并寻找 g 进行工作
   blocked       bool        // 用于判断m是否进行休眠状态
   park          note        // 通过这个休眠和唤醒 m 
   alllink       *m          // 所有 m 组成的一个链表
   schedlink     muintptr    // 下一个m,通过这个字段和 sched.midle 可以串成一个 m 的空闲链表
   mcache        *mcache     // mcache,m 拥有 p 的时候,会把自己的 mcache 给 p
   lockedg       guintptr    // lockedm 的对应值
   freelink      *m          // 待释放的 m 的list,通过 sched.freem 串成一个链表
}

G P M 三者之间的关系为:

三、菜鸟实战

实战需求:运行一个含有 goroutine 的程序

马上安排!

1、创建 g008.go

/*
 * @Author: 菜鸟实战
 * @FilePath: /go110/go-008/g008.go
 * @Description: goroutine
 */

package main

import (
	"fmt"
	"runtime"
	"time"
)

// 测试函数
func test_goroutine(from string) {
	for i := 0; i < 3; i++ {
		// 打印当前序号
		fmt.Println(from, ":", i)
	}
}

func main() {
	// 使用内置函数打印
	println("Hello", "菜鸟实战")

	// 开启一个 goroutine
	go test_goroutine("G")

	// 开启一个 goroutine
	go test_goroutine("M")

	// 开启一个 goroutine
	go func(msg string) {
		fmt.Println(msg)
	}("P")

	// 等待
	time.Sleep(time.Second)

	fmt.Println("done")

	// 当前版本
	fmt.Printf("版本: %s \n", runtime.Version())
}

2、编译和运行

# 1、生成模块依赖
go mod init g008
 
# 2、编译
go build g008.go 
 
# 3、编译后的目录结构
 
└── go-008
    ├── g008
    ├── g008.go
    └── go.mod
 
# 4、运行
go run g008

 

3、运行结果

Hello 菜鸟实战
G : 0
G : 1
P
G : 2
M : 0
M : 1
M : 2
done
版本: go1.17.10 

Hello 菜鸟实战
G : 0
G : 1
G : 2
P
M : 0
M : 1
M : 2
done
版本: go1.17.10 

Hello 菜鸟实战
P
G : 0
G : 1
G : 2
M : 0
M : 1
M : 2
done
版本: go1.17.10 

 菜鸟实战,持续学习!

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

菜鸟实战

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值