教程集 www.jiaochengji.com
教程集 >  Golang编程  >  golang教程  >  正文 goroutine原理的图文详解

goroutine原理的图文详解

发布时间:2021-12-13   编辑:jiaochengji.com
教程集为您提供goroutine原理的图文详解等资源,欢迎您收藏本站,我们将为您提供最新的goroutine原理的图文详解资源
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;"><path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"/></svg><h1>Go并发模型</h1> <pre><code> Don’t communicate by sharing memory; share memory by communicating. 不要以共享内存的⽅式来通信,要通过通信来共享内存。 </code></pre> <h1>Go调度器GMP</h1>

Go语⾔运⾏时环境提供了⾮常强⼤的管理goroutine和系统内核线程的调度器, 内部提供了三种
对象: Goroutine, Machine, Processor。

<ul><li>Goroutine : 指应⽤创建的goroutine</li><li>Machine : 指系统内核线程。</li><li>Processor : 指承载多个goroutine的运⾏器</li></ul>

在宏观上说, Goroutine与Machine因为Processor的存在,形成了多对多(M:N)的关系。 M个
⽤户线程对应N个系统线程,缺点增加了调度器的实现难度。

Goroutine是Go语⾔中并发的执⾏单位。 Goroutine底层是使⽤协程(coroutine)实现, coroutine
是⼀种运⾏在⽤户态的⽤户线程(参考操作系统原理:内核态,⽤户态)它可以由语⾔和框架层
调度。 Go在语⾔层⾯实现了调度器,同时对⽹络, IO库进⾏了封装处理,屏蔽了操作系统层⾯的
复杂的细节,在语⾔层⾯提供统⼀的关键字⽀持。

三者与内核级线程的关系如下图所示:

<ul><li>

⼀个Machine会对应⼀个内核线程(K),同时会有⼀个Processor与它绑定。⼀个Processor连接
⼀个或者多个Goroutine。 Processor有⼀个运⾏时的Goroutine(上图中绿⾊的G),其它的
Goroutine处于等待状态。

</li><li>

Processor的数量同时可以并发任务的数量,可通过GOMAXPROCS限制同时执⾏⽤户级任务的操
作系统线程。 GOMAXPROCS值默认是CPU的可⽤核⼼数,但是其数量是可以指定的。

</li><li>

当⼀个Goroutine创建被创建时, Goroutine对象被压⼊Processor的本地队列或者Go运⾏时
全局Goroutine队列。

</li><li>

Processor唤醒⼀个Machine,如果Machine的waiting队列没有等待被 唤醒的Machine,则
创建⼀个(只要不超过Machine的最⼤值, 10000), Processor获取到Machine后,与此
Machine绑定,并执⾏此Goroutine。

</li><li>

Machine执⾏过程中,随时会发⽣上下⽂切换。当发⽣上下⽂切换时,需要对执⾏现场进⾏
保护,以便下次被调度执⾏时进⾏现场恢复。 Go调度器中Machine的栈保存在Goroutine对
象上,只需要将Machine所需要的寄存器(堆栈指针、程序计数器等)保存到Goroutine对象上
即可。

</li><li>

如果此时Goroutine任务还没有执⾏完, Machine可以将Goroutine重新压⼊Processor的队
列,等待下⼀次被调度执⾏。

</li><li>

如果执⾏过程遇到阻塞并阻塞超时, Machine会与Processor分离,并等待阻塞结束。此时
Processor可以继续唤醒Machine执⾏其它的Goroutine,当阻塞结束时, Machine会尝
试”偷取”⼀个Processor,如果失败,这个Goroutine会被加⼊到全局队列中,然后Machine
将⾃⼰转⼊Waiting队列,等待被再次唤醒。

</li></ul><h1>
channel数据结构</h1>

Go依赖于称为CSP(Communicating Sequential Processes)的并发模型,通过
Channel实现这种同步模式。

<ul><li>channel结构体:</li></ul><pre><code class="lang-go hljs"><span class="token comment">//path: src/runtime/chan.go</span> <span class="token keyword">type</span> hchan <span class="token keyword">struct</span> <span class="token punctuation">{</span> qcount <span class="token builtin">uint</span> <span class="token comment">// 当前队列中剩余元素个数</span> dataqsiz <span class="token builtin">uint</span> <span class="token comment">// 环形队列⻓度,即可以存放的元素个数</span> buf unsafe<span class="token punctuation">.</span>Pointer <span class="token comment">// 环形队列指针</span> elemsize <span class="token builtin">uint16</span> <span class="token comment">// 每个元素的⼤⼩</span> closed <span class="token builtin">uint32</span> <span class="token comment">// 标识关闭状态</span> elemtype <span class="token operator">*</span>_type <span class="token comment">// 元素类型</span> sendx <span class="token builtin">uint</span> <span class="token comment">// 队列下标,指示元素写⼊时存放到队列中的位置</span> recvx <span class="token builtin">uint</span> <span class="token comment">// 队列下标,指示元素从队列的该位置读出</span> recvq waitq <span class="token comment">// 等待读消息的goroutine队列</span> sendq waitq <span class="token comment">// 等待写消息的goroutine队列</span> lock mutex <span class="token comment">// 互斥锁, chan不允许并发读写</span> <span class="token punctuation">}</span> </code></pre> <ul><li>

向channel写数据

<pre><code> 向⼀个channel中写数据简单过程如下: 1. 如果等待接收队列recvq不为空,说明缓冲区中没有数据或者没有缓冲区,此时直接从recvq取出G,并把数据写⼊,最后把该G唤醒,结束发送过程; 2. 如果缓冲区中有空余位置,将数据写⼊缓冲区,结束发送过程; 3. 如果缓冲区中没有空余位置,将待发送数据写⼊G,将当前G加⼊sendq,进⼊睡眠,等待被读goroutine唤醒; </code></pre>

</li><li>

从channel读数据

<pre><code> 从⼀个channel读数据简单过程如下: 1. 如果等待发送队列sendq不为空,且没有缓冲区,直接从sendq中取出G,把G中数据读出,最后把G唤醒,结束读取过程; 2. 如果等待发送队列sendq不为空,此时说明缓冲区已满,从缓冲区中⾸部读出数据,把G中数据写⼊缓冲区尾部,把G唤醒,结束读取过程; 3. 如果缓冲区中有数据,则从缓冲区取出数据,结束读取过程; 4. 将当前goroutine加⼊recvq,进⼊睡眠,等待被写goroutine唤醒; </code></pre> </li></ul>

<ul><li>

关闭channel

<pre><code> 关闭channel时会把recvq中的G全部唤醒,本该写⼊G的数据位置为nil。把sendq中的G全部唤醒,但这些G会panic。 除此之外, panic出现的常⻅场景还有: 1. 关闭值为nil的channel 2. 关闭已经被关闭的channel 3. 向已经关闭的channel写数据 </code></pre> </li></ul> 到此这篇关于“goroutine原理的图文详解”的文章就介绍到这了,更多文章或继续浏览下面的相关文章,希望大家以后多多支持JQ教程网!

您可能感兴趣的文章:
golang 深入浅出之 goroutine 理解
Goroutine的调度分析(一)
Go:Goroutine 的切换过程实际上涉及了什么
[Go 教程系列笔记] goroutine(协程)
goroutine 调度器
golang goroutine 通知_深入golang之---goroutine并发控制与通信
goroutine原理的图文详解
GO 语言之 Goroutine 原理解析
Golang中Goroutine与线程
Goroutine并发调度模型深度解析&amp;手撸一个协程池

[关闭]
~ ~