教程集 www.jiaochengji.com
教程集 >  Golang编程  >  golang教程  >  正文 golang的select实现原理剖析

golang的select实现原理剖析

发布时间:2022-01-08   编辑:jiaochengji.com
教程集为您提供golang的select实现原理剖析等资源,欢迎您收藏本站,我们将为您提供最新的golang的select实现原理剖析资源
<h1>写在最前面</h1>

select为golang提供了多路IO复用机制,和其他IO复用一样,用于检测是否有读写事件是否ready。

本文将介绍一下golang的select的用法和实现原理。

<h1 id="h1_7">实现原理</h1>

golang实现select的时候,实际上为每一个case语句定义了一个数据结构,select语句块执行的时候,实际上可以类比成对一个case数组处理的代码块(或者函数),然后程序流程转到选中的case块。

<h2 id="h2_8">case数据结构</h2>

源码包<code>src/runtime/select.go:scase</code>定义了表示case语句的数据结构:

<pre class="brush:go;gutter:true;">type scase struct { c *hchan // chan kind uint16 elem unsafe.Pointer // data element } </pre>

  scase.c表示当前case语句操作的chan指针,这也表明一个case只能监听一个chan。

  scase.kind表示当前的chan是可读还是可写channel或者是default。三种类型分别由常量定义:

<ul><li>caseRecv:case语句中尝试读取scase.c中的数据;</li><li>caseSend:case语句中尝试向scase.c中写入数据;</li><li>caseDefault: default语句</li></ul>

  scase.elem表示缓冲区地址,跟据scase.kind不同,有不同的用途:

<ul><li>scase.kind == caseRecv : scase.elem表示读出channel的数据存放地址;</li><li>scase.kind == caseSend : scase.elem表示将要写入channel的数据存放地址;</li></ul><h2 id="h2_9">select实现逻辑</h2>

源码包<code>src/runtime/select.go:selectgo()</code>定义了select选择case的函数:

<pre class="brush:go;gutter:true;">// selectgo implements the select statement. // // *sel is on the current goroutine's stack (regardless of any // escaping in selectgo). // // selectgo returns the index of the chosen scase, which matches the // ordinal position of its respective select{recv,send,default} call. func selectgo(sel *hselect) int {
} </pre>

  其中数据结构hselect如下:

<pre class="brush:go;gutter:true;">// Select statement header. // Known to compiler. // Changes here must also be made in src/cmd/internal/gc/select.go's selecttype. type hselect struct { tcase uint16 // total count of scase[] ncase uint16 // currently filled scase[] pollorder *uint16 // case poll order lockorder *uint16 // channel lock order scase [1]scase // one per case (in order of appearance) } </pre>

hselect.tcase存的是scase总数。

hselect.pollorder是保存scase的随机后的序列。以达到随机检测case的目的。

hselect.lockorder是保存的channel地址。所有case语句中channel序列,以达到去重防止对channel加锁时重复加锁的目的。

selectgo返回int,表示选中的scase,也就是ready的channel index。

该函数执行逻辑大致如下:

<span class="hljs-comment">1. 锁定scase语句中所有的channel </span>

<span class="hljs-comment"><span class="hljs-comment">2. 按照随机顺序检测scase中的channel是否ready </span></span>

<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment">  2.1 如果case可读,则读取channel中数据,解锁所有的channel,然后返回(case index) </span></span></span>

<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment">  <span class="hljs-comment">2.2 如果case可写,则将数据写入channel,解锁所有的channel,然后返回(case index)<span class="hljs-comment">
</span></span></span></span></span>

<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment">  2.3 所有case都未ready,则解锁所有的channel,然后返回(default index)</span></span></span></span></span>

<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment">3. 所有case都未ready,且没有default语句 </span></span></span></span></span></span>

<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment">  <span class="hljs-comment"> 3.1 将当前协程加入到所有channel的等待队列 </span></span></span></span></span></span></span>

<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment">   3.2 当将协程转入阻塞,等待被唤醒 </span></span></span></span></span></span></span></span>

<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment">4. 唤醒后返回channel对应的case index <span class="hljs-comment">
</span></span></span></span></span></span></span></span></span></span>

<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment">  4.1 如果是读操作,解锁所有的channel,然后返回(case index) </span></span></span></span></span></span></span></span></span></span>

<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment">  <span class="hljs-comment">4.2 如果是写操作,解锁所有的channel,然后返回(case index)</span></span></span></span></span></span></span></span></span></span></span>

转载于:https://www.cnblogs.com/howo/p/10507934.html

到此这篇关于“golang的select实现原理剖析”的文章就介绍到这了,更多文章或继续浏览下面的相关文章,希望大家以后多多支持JQ教程网!

您可能感兴趣的文章:
Golang 大杀器之性能剖析 PProf
Golang 源码剖析:fmt 标准库 --- Print* 是怎么样输出的?
golang的select实现原理剖析
Go Ticker实现原理剖析(轻松掌握Ticker实现原理)
[golang] 为什么接口变量的值不是nil?
基于Golang协程实现流量统计系统
golang channel 最详细的源码剖析
Golang package sync 剖析(三):sync.Cond
Golang package sync 剖析(一): sync.Once
理解 Golang 中函数调用的原理

[关闭]
~ ~