深入理解 Go 语言的 GMP 调度模型

        GMP 调度模型,解释起来很简单,G ( goroutine ) 代表协程,M ( machine ) 代表线程, P(processor) 代表逻辑处理器。

1. Go 语言并发编程入门

        Go 语言天然具备并发特性,基于 go 关键字就能很方便地创建一个可以并发执行的协程。什么场景下需要协程来并发执行呢?假设有这样一个服务或者接口:需要从其他三个服务获取数据(这三个服务之间没有互相依赖),处理后返回给客户端。

        这时候如何处理呢?如果使用 PHP 语言开发,只能顺序调用这些服务获取数据;如果使用的是 Java 之类的多线程语言,为了提高接口性能,通常可能会开启多个线程并发获取数据。对于 Go 语言来说,我们可以开启多个协程去并发获取数据。Go 语言实现这一需求的程序示例如下所示:

package main

import (
	"fmt"
	"sync"
)

func asyncWork(workId int, wg *sync.WaitGroup) {
	// 开始异步任务
	wg.Add(1)
	go func() {
		fmt.Println(fmt.Sprintf("work %d exec", workId))
		//异步任务结束
		wg.Done()
	}()
}

func main() {
	// WaitGroup 用于协程并发控制
	wg := sync.WaitGroup{}
	//启动 3 个协程并发执行任务
	for i := 0; i < 3; i++ {
		asyncWork(i, &wg)
	}
	//主协程等待任务结束
	wg.Wait()
	fmt.Println("work end")
}

程序运行结果如下所示:

work 2 exec
work 1 exec
work 0 exec
work end

         参考上面的代码,我们创建了 3 个子协程去并发执行任务。子协程模拟从第三方服务获取数据的功能,主协程需要等待 3 个子协程都执行结束,相当于等待其他 3 个服务返回数据。注意,这里使用 sync.WaitGroup 实现了等待功能,这需要我们在创建子协程之前调用一下函数 wg.Add(标识一个异步子协程执行结束)。主协程调用函数 wg.Wait 之后将会一直阻塞,直到所有的异步子协程执行结束,主协程才能恢复执行。 

        这里需要再强调一点,main 函数默认在主协程执行,而且一旦 main 函数执行结束,也意味着主协程执行结束,整个 Go 程序就会结束。所以如果删除了 wg.Wait() 这一行代码,你会发现子协程不一定能执行,因为子协程可能还没有被调度,Go 程序就结束了。

        还有一个问题,主协程如何获取子协程的返回数据呢?毕竟在我们的需求里,主协程是需要汇总处理 3 个子协程的返回数据的。想想最简单的方式,能不能通过在函数 asyncWork 中添加一个指针类型的输入参数作为返回值呢?当然可以。

        其实也可以通过管道实现主协程和子协程之间的数据传递,Go 语言管道的设计初衷就是实现协程间的数据传递。想象一下,管道就像一根水管,数据从管道的一端流入,从另外一端流出。基于管道改造一下上面的程序,如下所示: 

package mai
  • 28
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Mindfulness code

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

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

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

打赏作者

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

抵扣说明:

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

余额充值