goroutine实践分享
创始人
2024-05-31 21:12:34
0

一.goroutine原理及GPM调度模型

  • 架构图

    GPM

  • 并发模型

​ 并发模型分为用户线程模型、内核线程模型、两级线程模型

​ 用户线程模型是用户线程和内核线程一对多映射模型。多个用户线程绑定到一个内核线程上,线程调度由用户线程库实现,系统对线程无感知,该模型线程调度由于不需要用户态到内核态切换,实现起来比较轻量,资源消耗少,但该模型中所有线程都绑定到同一个内核线程上,并没有实现真正的并发。

​ 内核线程模型是用户线程和内核线程一对一映射模型。内核线程模型实现相对简单,一个用户线程绑定到一个内核线程上,线程调度由系统实现,实现了真正意义上的并发,但在不同线程做切换需要用户态到内核态切换,资源消耗较大,对性能影响很大。

​ 两级线程模型是用户线程和内核线程n对m映射模型。该模型中n个用户线程和m个内核线程实现动态绑定,实现了系统级和用户级的两级调度模型,系统调度器调度内核线程,用户调度器调度用户级线程保证每个用户线 程公平调度,该模型实现了内核线程池的效果,可以大大减少内核线程创建、销毁的资源消耗和性能损失。

  • G-P-M模型

​ goroutine实现了两级线程模型,每一个go关键字都创建一个用户级线程,每一个被创建的线程都是一个可调度的执行单元。内核线程创建需要2mb的固定栈空间,而每一个go线程只需要2kb的栈空间,随着任务的执行,栈空间不足时,调度器会动态的给go线程分配另一块连续栈空间。内核线程的调度需要用户态到内核态的切换,非常耗费资源,而go线程调度由runtime在用户级别细线,资源消耗很少。

​ goroutine模型,抽象为三个结构G、P、M,G代表一个用户级别线程,G保存任务执行相关的堆栈和上线文状态。P代表一个逻辑处理器,每个P上都有一个G执行队列,P的数量决定了系统最大并行度,P的存在达到了内核线程池的效果。M代表一个系统线程,每个M在绑定一个P后开始执行上面的G,M不保存G的状态,这是G可以跨M调度的基础。

  • G-P-M调度

​ goroutine队列分为global任务队列和P维护的local任务队列。当通过go关键字创建一个新的goroutine的时候,它会优先被放入P的本地队列。为了运行goroutine,M需要持有(绑定)一个P,接着M会启动一个OS线程,循环从P的本地队列里取出一个goroutine并执行。当然还有上文提及的 work-stealing调度算法:当M执行完了当前P的Local队列里的所有G后,P也不会就这么在那躺尸啥都不干,它会先尝试从Global队列寻找G来执行,如果Global队列为空,它会随机挑选另外一个P,从它的队列里中拿走一半的G到自己的队列中执行。

  1. 用户态阻塞处理
    goroutine因为channle或sync同步阻塞时,该goroutine会被放置到某个wait队列,该G的状态由_Gruning变为_Gwaitting,而M会跳过该G尝试获取并执行下一个G,如果此时没有runnable的G供M运行,那么M将解绑P,并进入sleep状态;当阻塞的G被另一端的G2唤醒时,G被标记为runnable,尝试加入G2所在P的runnext,然后再是P的Local队列和Global队列。
  2. 内核态阻塞处理
    当G被阻塞在某个系统调用上时,此时G会阻塞在_Gsyscall状态,M也处于 block on syscall 状态,此时的M可被抢占调度:执行该G的M会与P解绑,而P则尝试与其它idle的M绑定,继续执行其它G。如果没有其它idle的M,但P的Local队列中仍然有G需要执行,则创建一个新的M;当系统调用完成后,G会重新尝试获取一个idle的P进入它的Local队列恢复执行,如果没有idle的P,G会被标记为runnable加入到Global队列。

二.应用实践

  • routinepool

    go协程虽然相比系统线程创建、销毁轻量仅需要2kb栈空间,协程间调度不需要用户态到内核态切换,但是如果大量频繁创建、销毁协程如10w、100w,则内存的消耗、协程调度cpu消耗仍需要浪费大量系统资源。这些消耗的资源大部分浪费在协程的创建、销毁上面,对此可以使用协程池技术,达到协程资源复用目的。

    routinepool协程库实现了固定协程池和弹性协程池两种协程池,简要介绍整体结构。

    协程池

type RoutinePool struct {......}func NewFixedRoutinePool(taskQueueSize int64, waitInterval time.Duration,normalWorkerSize int64) (rp *RoutinePool){ ...... }func NewCachedRoutinePool(taskQueueSize int64, waitInterval time.Duration,normalWorkerSize int64, maxWorkerSize int64) (rp *RoutinePool) {  ......  }func (rp *RoutinePool) Execute(task Runable) bool { ...... }func (rp *RoutinePool) ExecuteFunc(call func()) bool { ...... }func (rp *RoutinePool) Close() {  ......  }

​ 可执行任务接口

type Runable interface {Run()}

​ 工作协程

type Worker struct {......
}func NewWorker(tq chan Runable, ei time.Duration, rf bool, dc chan bool, cc chan struct{}) (w *Worker) {  ......  }func (w *Worker) Start() { ......  }

​ RoutinePool 对象实现了协程池管理功能,可以使用NewFixedRoutinePool创建固定协程池、NewCachedRoutinePool创建弹性协程池。执行的任务可以是实现Runable的接口或func()类型函数,通过Execute或ExecuteFunc函数放入协程池,等待worker队列执行。

  • gofuture

    go协程池使用虽然方便,但是创建后不容易控制,不能方便的终止其执行,也不能类似函数调用便捷的获取其执行结果。gofuture库对默认的go协程使用做了一层封装,可以控制协程的执行,获取协程执行结果。

    type Interface interface {Cancel()IsCancelled() boolGet() (interface{}, error)GetUntil(d time.Duration) (interface{}, bool, error)}func NewFuture(inFunc func() (interface{}, error)) Interface { ...... }
    

    NewFuture 产生一个内部实现了Interface接口的future对象。该对象提供了Get、GetUntil、Cancel、IsCancelled函数调用,Get函数获取inFunc执行结果;GetUntil函数同样是获取inFunc执行结果,但在inFunc未执行前会等待d时间段,超时则错误返回;Cancel函数取消inFunc任务执行;IsCancelled函数判断任务是否取消执行。

  • goscheduled

    在编程中经常会遇到某一类任务按照固定频率或者固定时间段多次执行,为此可以实现一个时间优先任务调度队列。

    队列管理对象

    type GoScheduled struct {......}func NewGoScheduled() (gs *GoScheduled) { ...... }func (gs *GoScheduled) ScheduledAtFixedRate(task routinepool.Runable, i time.Duration) bool { ......  }func (gs *GoScheduled) ScheduledAtFixedDelay(task routinepool.Runable, i time.Duration) bool { ...... }func (gs *GoScheduled) Close() { ...... }
    

    调度任务队列

type ScheduledQueue struct {......}func NewScheduledQueue() (sq *ScheduledQueue) { ...... }func (sq *ScheduledQueue) HeapPush(x interface{}) { ...... }func (sq *ScheduledQueue) HeapPop() (x interface{}) { ...... }

​ 调度任务

 	type Scheduled struct {......}func (s *Scheduled) Run() { ...... }

​ GoScheduled 实现了任务调度模块管理,可以通过ScheduledAtFixedRate函数调度固定频率任务的执行,ScheduledAtFixedDelay函数调度固定时间段间隔任务的执行。ScheduledQueue 使用优先队列实现任务按照时间优先级进行调度,HeapPush函数实现调度任务入队,HeapPop函数实现调度任务出队。Scheduled 实现一个具体可以被调度的任务。

相关内容

热门资讯

电视安卓系统哪个品牌好,哪家品... 你有没有想过,家里的电视是不是该升级换代了呢?现在市面上电视品牌琳琅满目,各种操作系统也是让人眼花缭...
安卓会员管理系统怎么用,提升服... 你有没有想过,手机里那些你爱不释手的APP,背后其实有个强大的会员管理系统在默默支持呢?没错,就是那...
安卓系统软件使用技巧,解锁软件... 你有没有发现,用安卓手机的时候,总有一些小技巧能让你玩得更溜?别小看了这些小细节,它们可是能让你的手...
安卓系统提示音替换 你知道吗?手机里那个时不时响起的提示音,有时候真的能让人心情大好,有时候又让人抓狂不已。今天,就让我...
安卓开机不了系统更新 手机突然开不了机,系统更新还卡在那里,这可真是让人头疼的问题啊!你是不是也遇到了这种情况?别急,今天...
安卓系统中微信视频,安卓系统下... 你有没有发现,现在用手机聊天,视频通话简直成了标配!尤其是咱们安卓系统的小伙伴们,微信视频功能更是用...
安卓系统是服务器,服务器端的智... 你知道吗?在科技的世界里,安卓系统可是个超级明星呢!它不仅仅是个手机操作系统,竟然还能成为服务器的得...
pc电脑安卓系统下载软件,轻松... 你有没有想过,你的PC电脑上安装了安卓系统,是不是瞬间觉得世界都大不一样了呢?没错,就是那种“一机在...
电影院购票系统安卓,便捷观影新... 你有没有想过,在繁忙的生活中,一部好电影就像是一剂强心针,能瞬间让你放松心情?而我今天要和你分享的,...
安卓系统可以写程序? 你有没有想过,安卓系统竟然也能写程序呢?没错,你没听错!这个我们日常使用的智能手机操作系统,竟然有着...
安卓系统架构书籍推荐,权威书籍... 你有没有想过,想要深入了解安卓系统架构,却不知道从何下手?别急,今天我就要给你推荐几本超级实用的书籍...
安卓系统看到的炸弹,技术解析与... 安卓系统看到的炸弹——揭秘手机中的隐形威胁在数字化时代,智能手机已经成为我们生活中不可或缺的一部分。...
鸿蒙系统有安卓文件,畅享多平台... 你知道吗?最近在科技圈里,有个大新闻可是闹得沸沸扬扬的,那就是鸿蒙系统竟然有了安卓文件!是不是觉得有...
宝马安卓车机系统切换,驾驭未来... 你有没有发现,现在的汽车越来越智能了?尤其是那些豪华品牌,比如宝马,它们的内饰里那个大屏幕,简直就像...
p30退回安卓系统 你有没有听说最近P30的用户们都在忙活一件大事?没错,就是他们的手机要退回安卓系统啦!这可不是一个简...
oppoa57安卓原生系统,原... 你有没有发现,最近OPPO A57这款手机在安卓原生系统上的表现真是让人眼前一亮呢?今天,就让我带你...
安卓系统输入法联想,安卓系统输... 你有没有发现,手机上的输入法真的是个神奇的小助手呢?尤其是安卓系统的输入法,简直就是智能生活的点睛之...
怎么进入安卓刷机系统,安卓刷机... 亲爱的手机控们,你是否曾对安卓手机的刷机系统充满好奇?想要解锁手机潜能,体验全新的系统魅力?别急,今...
安卓系统程序有病毒 你知道吗?在这个数字化时代,手机已经成了我们生活中不可或缺的好伙伴。但是,你知道吗?即使是安卓系统,...
奥迪中控安卓系统下载,畅享智能... 你有没有发现,现在汽车的中控系统越来越智能了?尤其是奥迪这种豪华品牌,他们的中控系统简直就是科技与艺...