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

Golang中select的实现原理

发布时间:2022-01-28   编辑:jiaochengji.com
教程集为您提供Golang中select的实现原理等资源,欢迎您收藏本站,我们将为您提供最新的Golang中select的实现原理资源
<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>前言</h1>

select是Golang在语言层面提供的多路IO复用的机制。与switch语句稍微有点相似,也会有case和最后的default选择支。每一个case代表一个通信操作(在某个channel上进行发送或者接收)并且会包含一些语句组成一个语句块。

<h1>
基本用法</h1>

select会等待case中能够执行的case时才去执行。当满足条件时。select才回去通信并执行case之后的语句,这时候其他通信是不执行的。如果有多个case同时就绪,select会随机选择一个执行。一个没有任何case的select语句,会永远等待下去。

<h2>
非阻塞select</h2> <pre><code class="lang-go hljs"><span class="token keyword">select</span> <span class="token punctuation">{</span> <span class="token keyword">case</span> <span class="token operator"><-</span>ch<span class="token punctuation">:</span> <span class="token keyword">default</span><span class="token punctuation">:</span> <span class="token punctuation">}</span> </code></pre> <h2>阻塞select</h2> <pre><code class="lang-go hljs"><span class="token keyword">select</span> <span class="token punctuation">{</span> <span class="token keyword">case</span> <span class="token operator"><-</span>ch<span class="token punctuation">:</span> <span class="token punctuation">}</span> </code></pre> <blockquote>

注意:对于读channel的case来说,如elem, ok := <-chan1:, 如果channel有可能被其他协程关闭的情况下,一定要检测读取是否成功,因为close的channel也有可能返回,此时ok == false,且不会阻塞

</blockquote> <h1>
源码分析</h1> <blockquote>

注意:我会把源码中每个方法的作用都注释出来,可以参考注释进行理解。

</blockquote> <h2>
数据结构</h2>

Golang实现select时,定义了一个数据结构表示每个case语句(含defaut,default实际上是一种特殊的case),select执行过程可以类比成一个函数,函数输入case数组,输出选中的case,然后程序流程转到选中的case块。
我们先看一下case数据结构

<blockquote>

runtime\select.go

</blockquote> <pre><code class="lang-go hljs"><span class="token keyword">type</span> scase <span class="token keyword">struct</span> <span class="token punctuation">{</span> c <span class="token operator">*</span>hchan <span class="token comment">// 当前case语句所操作的channel指针</span> elem unsafe<span class="token punctuation">.</span>Pointer <span class="token comment">// data element数据元素</span> kind <span class="token builtin">uint16</span> <span class="token comment">//表示该case的类型</span> pc <span class="token builtin">uintptr</span> <span class="token comment">// race pc (for race detector / msan)</span> releasetime <span class="token builtin">int64</span> <span class="token punctuation">}</span> </code></pre> <pre><code class="lang-go hljs"><span class="token comment">//scase.kind values.</span> <span class="token keyword">const</span> <span class="token punctuation">(</span> caseNil <span class="token operator">=</span> <span class="token boolean">iota</span> caseRecv caseSend caseDefault <span class="token punctuation">)</span> </code></pre>

scase.kind表示该case的类型,分别为:

<ul><li>caseRecv:case语句中尝试读取scase.c中的数据,case <-Chan;</li><li>caseSend:case语句中尝试向scase.c中写入数据,case Chan <- Send;</li><li>caseDefault:default</li></ul><h2>
select实现</h2> <pre><code class="lang-go hljs"><span class="token comment">// cas0为scase数组的首地址,selectgo()就是从这些scase中找出一个返回</span> <span class="token comment">// order0指向一个[2 * ncases] uint16类型的数组</span> <span class="token comment">// ncases表示scase数组的长度</span> <span class="token comment">//返回值:</span> <span class="token comment">//int :选中case的编号</span> <span class="token comment">//bool:是否成功从channle中读取了数据</span> <span class="token keyword">func</span> <span class="token function">selectgo</span><span class="token punctuation">(</span>cas0 <span class="token operator">*</span>scase<span class="token punctuation">,</span> order0 <span class="token operator">*</span><span class="token builtin">uint16</span><span class="token punctuation">,</span> ncases <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token builtin">int</span><span class="token punctuation">,</span> <span class="token builtin">bool</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> debugSelect <span class="token punctuation">{</span> <span class="token function">print</span><span class="token punctuation">(</span><span class="token string">"select: cas0="</span><span class="token punctuation">,</span> cas0<span class="token punctuation">,</span> <span class="token string">"\n"</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> cas1 <span class="token operator">:=</span> <span class="token punctuation">(</span><span class="token operator">*</span><span class="token punctuation">[</span><span class="token number">1</span> <span class="token operator"><<</span> <span class="token number">16</span><span class="token punctuation">]</span>scase<span class="token punctuation">)</span><span class="token punctuation">(</span>unsafe<span class="token punctuation">.</span><span class="token function">Pointer</span><span class="token punctuation">(</span>cas0<span class="token punctuation">)</span><span class="token punctuation">)</span> order1 <span class="token operator">:=</span> <span class="token punctuation">(</span><span class="token operator">*</span><span class="token punctuation">[</span><span class="token number">1</span> <span class="token operator"><<</span> <span class="token number">17</span><span class="token punctuation">]</span><span class="token builtin">uint16</span><span class="token punctuation">)</span><span class="token punctuation">(</span>unsafe<span class="token punctuation">.</span><span class="token function">Pointer</span><span class="token punctuation">(</span>order0<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">//将cas1从第一个元素开始切片,长度为ncases,容量为ncases</span> <span class="token comment">//a[x:y:z] 切片长度: y-x 切片容量:z-x</span> scases <span class="token operator">:=</span> cas1<span class="token punctuation">[</span><span class="token punctuation">:</span>ncases<span class="token punctuation">:</span>ncases<span class="token punctuation">]</span> <span class="token comment">//所有case轮询顺序,占了前面ncase</span> pollorder <span class="token operator">:=</span> order1<span class="token punctuation">[</span><span class="token punctuation">:</span>ncases<span class="token punctuation">:</span>ncases<span class="token punctuation">]</span> <span class="token comment">//所有case语句中channel序列,占了后面ncase</span> lockorder <span class="token operator">:=</span> order1<span class="token punctuation">[</span>ncases<span class="token punctuation">:</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token punctuation">:</span>ncases<span class="token punctuation">:</span>ncases<span class="token punctuation">]</span> <span class="token comment">// 将涉及零个通道的发送/接收案例替换为caseNil,因此以下逻辑可以假定为非nil通道。</span> <span class="token keyword">for</span> i <span class="token operator">:=</span> <span class="token keyword">range</span> scases <span class="token punctuation">{</span> cas <span class="token operator">:=</span> <span class="token operator">&</span>scases<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token keyword">if</span> cas<span class="token punctuation">.</span>c <span class="token operator">==</span> <span class="token boolean">nil</span> <span class="token operator">&&</span> cas<span class="token punctuation">.</span>kind <span class="token operator">!=</span> caseDefault <span class="token punctuation">{</span> <span class="token operator">*</span>cas <span class="token operator">=</span> scase<span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">var</span> t0 <span class="token builtin">int64</span> <span class="token keyword">if</span> blockprofilerate <span class="token operator">></span> <span class="token number">0</span> <span class="token punctuation">{</span> t0 <span class="token operator">=</span> <span class="token function">cputicks</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">for</span> i <span class="token operator">:=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> ncases<span class="token punctuation">;</span> i<span class="token operator"> </span> <span class="token punctuation">{</span> scases<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">.</span>releasetime <span class="token operator">=</span> <span class="token operator">-</span><span class="token number">1</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">// 生成排列顺序,pollorder重新排序</span> <span class="token keyword">for</span> i <span class="token operator">:=</span> <span class="token number">1</span><span class="token punctuation">;</span> i <span class="token operator"><</span> ncases<span class="token punctuation">;</span> i<span class="token operator"> </span> <span class="token punctuation">{</span> j <span class="token operator">:=</span> <span class="token function">fastrandn</span><span class="token punctuation">(</span><span class="token function">uint32</span><span class="token punctuation">(</span>i <span class="token operator"> </span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">)</span> pollorder<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token operator">=</span> pollorder<span class="token punctuation">[</span>j<span class="token punctuation">]</span> pollorder<span class="token punctuation">[</span>j<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">uint16</span><span class="token punctuation">(</span>i<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token comment">//通过Hchan地址进行排序以获得锁定顺序</span> <span class="token comment">//采用简单的堆排序,以确保n log n个时间和恒定的堆栈占用量</span> <span class="token keyword">for</span> i <span class="token operator">:=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> ncases<span class="token punctuation">;</span> i<span class="token operator"> </span> <span class="token punctuation">{</span> j <span class="token operator">:=</span> i <span class="token comment">// 替换相同channel 的case,以达到去重防止对channel加锁时重复加锁的目的</span> c <span class="token operator">:=</span> scases<span class="token punctuation">[</span>pollorder<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">.</span>c <span class="token keyword">for</span> j <span class="token operator">></span> <span class="token number">0</span> <span class="token operator">&&</span> scases<span class="token punctuation">[</span>lockorder<span class="token punctuation">[</span><span class="token punctuation">(</span>j<span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token operator">/</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">.</span>c<span class="token punctuation">.</span><span class="token function">sortkey</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator"><</span> c<span class="token punctuation">.</span><span class="token function">sortkey</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> k <span class="token operator">:=</span> <span class="token punctuation">(</span>j <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">/</span> <span class="token number">2</span> lockorder<span class="token punctuation">[</span>j<span class="token punctuation">]</span> <span class="token operator">=</span> lockorder<span class="token punctuation">[</span>k<span class="token punctuation">]</span> j <span class="token operator">=</span> k <span class="token punctuation">}</span> lockorder<span class="token punctuation">[</span>j<span class="token punctuation">]</span> <span class="token operator">=</span> pollorder<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token punctuation">}</span> <span class="token keyword">for</span> i <span class="token operator">:=</span> ncases <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">;</span> i <span class="token operator">>=</span> <span class="token number">0</span><span class="token punctuation">;</span> i<span class="token operator">--</span> <span class="token punctuation">{</span> o <span class="token operator">:=</span> lockorder<span class="token punctuation">[</span>i<span class="token punctuation">]</span> c <span class="token operator">:=</span> scases<span class="token punctuation">[</span>o<span class="token punctuation">]</span><span class="token punctuation">.</span>c lockorder<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token operator">=</span> lockorder<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> j <span class="token operator">:=</span> <span class="token number">0</span> <span class="token keyword">for</span> <span class="token punctuation">{</span> k <span class="token operator">:=</span> j<span class="token operator">*</span><span class="token number">2</span> <span class="token operator"> </span> <span class="token number">1</span> <span class="token keyword">if</span> k <span class="token operator">>=</span> i <span class="token punctuation">{</span> <span class="token keyword">break</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> k<span class="token operator"> </span><span class="token number">1</span> <span class="token operator"><</span> i <span class="token operator">&&</span> scases<span class="token punctuation">[</span>lockorder<span class="token punctuation">[</span>k<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">.</span>c<span class="token punctuation">.</span><span class="token function">sortkey</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator"><</span> scases<span class="token punctuation">[</span>lockorder<span class="token punctuation">[</span>k<span class="token operator"> </span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">.</span>c<span class="token punctuation">.</span><span class="token function">sortkey</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> k<span class="token operator"> </span> <span class="token punctuation">}</span> <span class="token keyword">if</span> c<span class="token punctuation">.</span><span class="token function">sortkey</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator"><</span> scases<span class="token punctuation">[</span>lockorder<span class="token punctuation">[</span>k<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">.</span>c<span class="token punctuation">.</span><span class="token function">sortkey</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> lockorder<span class="token punctuation">[</span>j<span class="token punctuation">]</span> <span class="token operator">=</span> lockorder<span class="token punctuation">[</span>k<span class="token punctuation">]</span> j <span class="token operator">=</span> k <span class="token keyword">continue</span> <span class="token punctuation">}</span> <span class="token keyword">break</span> <span class="token punctuation">}</span> lockorder<span class="token punctuation">[</span>j<span class="token punctuation">]</span> <span class="token operator">=</span> o <span class="token punctuation">}</span> <span class="token keyword">if</span> debugSelect <span class="token punctuation">{</span> <span class="token keyword">for</span> i <span class="token operator">:=</span> <span class="token number">0</span><span class="token punctuation">;</span> i<span class="token operator"> </span><span class="token number">1</span> <span class="token operator"><</span> ncases<span class="token punctuation">;</span> i<span class="token operator"> </span> <span class="token punctuation">{</span> <span class="token keyword">if</span> scases<span class="token punctuation">[</span>lockorder<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">.</span>c<span class="token punctuation">.</span><span class="token function">sortkey</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">></span> scases<span class="token punctuation">[</span>lockorder<span class="token punctuation">[</span>i<span class="token operator"> </span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">.</span>c<span class="token punctuation">.</span><span class="token function">sortkey</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">print</span><span class="token punctuation">(</span><span class="token string">"i="</span><span class="token punctuation">,</span> i<span class="token punctuation">,</span> <span class="token string">" x="</span><span class="token punctuation">,</span> lockorder<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token string">" y="</span><span class="token punctuation">,</span> lockorder<span class="token punctuation">[</span>i<span class="token operator"> </span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token string">"\n"</span><span class="token punctuation">)</span> <span class="token function">throw</span><span class="token punctuation">(</span><span class="token string">"select: broken sort"</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">//锁住所有的channel</span> <span class="token function">sellock</span><span class="token punctuation">(</span>scases<span class="token punctuation">,</span> lockorder<span class="token punctuation">)</span> <span class="token keyword">var</span> <span class="token punctuation">(</span> gp <span class="token operator">*</span>g sg <span class="token operator">*</span>sudog c <span class="token operator">*</span>hchan k <span class="token operator">*</span>scase sglist <span class="token operator">*</span>sudog sgnext <span class="token operator">*</span>sudog qp unsafe<span class="token punctuation">.</span>Pointer nextp <span class="token operator">*</span><span class="token operator">*</span>sudog <span class="token punctuation">)</span> loop<span class="token punctuation">:</span> <span class="token comment">// pass 1 - look for something already waiting</span> <span class="token comment">// 按照随机顺序检测scase中的channel是否ready</span> <span class="token keyword">var</span> dfli <span class="token builtin">int</span> <span class="token keyword">var</span> dfl <span class="token operator">*</span>scase <span class="token keyword">var</span> casi <span class="token builtin">int</span> <span class="token keyword">var</span> cas <span class="token operator">*</span>scase <span class="token keyword">var</span> recvOK <span class="token builtin">bool</span> <span class="token comment">//开始遍历case数组了</span> <span class="token keyword">for</span> i <span class="token operator">:=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> ncases<span class="token punctuation">;</span> i<span class="token operator"> </span> <span class="token punctuation">{</span> casi <span class="token operator">=</span> <span class="token function">int</span><span class="token punctuation">(</span>pollorder<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">)</span> cas <span class="token operator">=</span> <span class="token operator">&</span>scases<span class="token punctuation">[</span>casi<span class="token punctuation">]</span> c <span class="token operator">=</span> cas<span class="token punctuation">.</span>c <span class="token keyword">switch</span> cas<span class="token punctuation">.</span>kind <span class="token punctuation">{</span> <span class="token keyword">case</span> caseNil<span class="token punctuation">:</span> <span class="token keyword">continue</span> <span class="token comment">// 接收chan</span> <span class="token keyword">case</span> caseRecv<span class="token punctuation">:</span> sg <span class="token operator">=</span> c<span class="token punctuation">.</span>sendq<span class="token punctuation">.</span><span class="token function">dequeue</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 当chan的等待写队列不为空,需要等待</span> <span class="token keyword">if</span> sg <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span> <span class="token keyword">goto</span> recv <span class="token punctuation">}</span> <span class="token comment">//当chan的缓存队列存在元素时,不需要等待</span> <span class="token keyword">if</span> c<span class="token punctuation">.</span>qcount <span class="token operator">></span> <span class="token number">0</span> <span class="token punctuation">{</span> <span class="token keyword">goto</span> bufrecv <span class="token punctuation">}</span> <span class="token comment">// 当chan关闭时</span> <span class="token keyword">if</span> c<span class="token punctuation">.</span>closed <span class="token operator">!=</span> <span class="token number">0</span> <span class="token punctuation">{</span> <span class="token keyword">goto</span> rclose <span class="token punctuation">}</span> <span class="token comment">//发送chan</span> <span class="token keyword">case</span> caseSend<span class="token punctuation">:</span> <span class="token keyword">if</span> raceenabled <span class="token punctuation">{</span> <span class="token function">racereadpc</span><span class="token punctuation">(</span>c<span class="token punctuation">.</span><span class="token function">raceaddr</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> cas<span class="token punctuation">.</span>pc<span class="token punctuation">,</span> chansendpc<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token comment">// 当chan关闭时</span> <span class="token keyword">if</span> c<span class="token punctuation">.</span>closed <span class="token operator">!=</span> <span class="token number">0</span> <span class="token punctuation">{</span> <span class="token keyword">goto</span> sclose <span class="token punctuation">}</span> <span class="token comment">//当chan的等待读消息的队列不为空,需要等待</span> sg <span class="token operator">=</span> c<span class="token punctuation">.</span>recvq<span class="token punctuation">.</span><span class="token function">dequeue</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">if</span> sg <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span> <span class="token keyword">goto</span> send <span class="token punctuation">}</span> <span class="token comment">// chan的缓存队列的元素少于缓存容量时,还有位置,不需要等待</span> <span class="token keyword">if</span> c<span class="token punctuation">.</span>qcount <span class="token operator"><</span> c<span class="token punctuation">.</span>dataqsiz <span class="token punctuation">{</span> <span class="token keyword">goto</span> bufsend <span class="token punctuation">}</span> <span class="token keyword">case</span> caseDefault<span class="token punctuation">:</span> dfli <span class="token operator">=</span> casi dfl <span class="token operator">=</span> cas <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> dfl <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span> <span class="token function">selunlock</span><span class="token punctuation">(</span>scases<span class="token punctuation">,</span> lockorder<span class="token punctuation">)</span> casi <span class="token operator">=</span> dfli cas <span class="token operator">=</span> dfl <span class="token keyword">goto</span> retc <span class="token punctuation">}</span> <span class="token comment">// pass 2 - enqueue on all chans</span> <span class="token comment">//所有case都未ready,且没有default语句</span> <span class="token comment">//将当前协程加入到所有channel的等待队列</span> gp <span class="token operator">=</span> <span class="token function">getg</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">if</span> gp<span class="token punctuation">.</span>waiting <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span> <span class="token function">throw</span><span class="token punctuation">(</span><span class="token string">"gp.waiting != nil"</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> nextp <span class="token operator">=</span> <span class="token operator">&</span>gp<span class="token punctuation">.</span>waiting <span class="token keyword">for</span> <span class="token boolean">_</span><span class="token punctuation">,</span> casei <span class="token operator">:=</span> <span class="token keyword">range</span> lockorder <span class="token punctuation">{</span> casi <span class="token operator">=</span> <span class="token function">int</span><span class="token punctuation">(</span>casei<span class="token punctuation">)</span> cas <span class="token operator">=</span> <span class="token operator">&</span>scases<span class="token punctuation">[</span>casi<span class="token punctuation">]</span> <span class="token keyword">if</span> cas<span class="token punctuation">.</span>kind <span class="token operator">==</span> caseNil <span class="token punctuation">{</span> <span class="token keyword">continue</span> <span class="token punctuation">}</span> c <span class="token operator">=</span> cas<span class="token punctuation">.</span>c sg <span class="token operator">:=</span> <span class="token function">acquireSudog</span><span class="token punctuation">(</span><span class="token punctuation">)</span> sg<span class="token punctuation">.</span>g <span class="token operator">=</span> gp sg<span class="token punctuation">.</span>isSelect <span class="token operator">=</span> <span class="token boolean">true</span> <span class="token comment">// No stack splits between assigning elem and enqueuing</span> <span class="token comment">// sg on gp.waiting where copystack can find it.</span> sg<span class="token punctuation">.</span>elem <span class="token operator">=</span> cas<span class="token punctuation">.</span>elem sg<span class="token punctuation">.</span>releasetime <span class="token operator">=</span> <span class="token number">0</span> <span class="token keyword">if</span> t0 <span class="token operator">!=</span> <span class="token number">0</span> <span class="token punctuation">{</span> sg<span class="token punctuation">.</span>releasetime <span class="token operator">=</span> <span class="token operator">-</span><span class="token number">1</span> <span class="token punctuation">}</span> sg<span class="token punctuation">.</span>c <span class="token operator">=</span> c <span class="token comment">// Construct waiting list in lock order.</span> <span class="token operator">*</span>nextp <span class="token operator">=</span> sg nextp <span class="token operator">=</span> <span class="token operator">&</span>sg<span class="token punctuation">.</span>waitlink <span class="token keyword">switch</span> cas<span class="token punctuation">.</span>kind <span class="token punctuation">{</span> <span class="token keyword">case</span> caseRecv<span class="token punctuation">:</span> <span class="token comment">// 加入等待接收队列</span> c<span class="token punctuation">.</span>recvq<span class="token punctuation">.</span><span class="token function">enqueue</span><span class="token punctuation">(</span>sg<span class="token punctuation">)</span> <span class="token keyword">case</span> caseSend<span class="token punctuation">:</span> <span class="token comment">// 加入等待发送队列</span> c<span class="token punctuation">.</span>sendq<span class="token punctuation">.</span><span class="token function">enqueue</span><span class="token punctuation">(</span>sg<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">// wait for someone to wake us up</span> <span class="token comment">//当将协程转入阻塞,等待被唤醒</span> gp<span class="token punctuation">.</span>param <span class="token operator">=</span> <span class="token boolean">nil</span> <span class="token function">gopark</span><span class="token punctuation">(</span>selparkcommit<span class="token punctuation">,</span> <span class="token boolean">nil</span><span class="token punctuation">,</span> waitReasonSelect<span class="token punctuation">,</span> traceEvGoBlockSelect<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token function">sellock</span><span class="token punctuation">(</span>scases<span class="token punctuation">,</span> lockorder<span class="token punctuation">)</span> gp<span class="token punctuation">.</span>selectDone <span class="token operator">=</span> <span class="token number">0</span> sg <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token operator">*</span>sudog<span class="token punctuation">)</span><span class="token punctuation">(</span>gp<span class="token punctuation">.</span>param<span class="token punctuation">)</span> gp<span class="token punctuation">.</span>param <span class="token operator">=</span> <span class="token boolean">nil</span> <span class="token comment">// pass 3 - dequeue from unsuccessful chans</span> <span class="token comment">// otherwise they stack up on quiet channels</span> <span class="token comment">// record the successful case, if any.</span> <span class="token comment">// We singly-linked up the SudoGs in lock order.</span> <span class="token comment">//唤醒后返回channel对应的case index</span> casi <span class="token operator">=</span> <span class="token operator">-</span><span class="token number">1</span> cas <span class="token operator">=</span> <span class="token boolean">nil</span> sglist <span class="token operator">=</span> gp<span class="token punctuation">.</span>waiting <span class="token comment">// Clear all elem before unlinking from gp.waiting.</span> <span class="token keyword">for</span> sg1 <span class="token operator">:=</span> gp<span class="token punctuation">.</span>waiting<span class="token punctuation">;</span> sg1 <span class="token operator">!=</span> <span class="token boolean">nil</span><span class="token punctuation">;</span> sg1 <span class="token operator">=</span> sg1<span class="token punctuation">.</span>waitlink <span class="token punctuation">{</span> sg1<span class="token punctuation">.</span>isSelect <span class="token operator">=</span> <span class="token boolean">false</span> sg1<span class="token punctuation">.</span>elem <span class="token operator">=</span> <span class="token boolean">nil</span> sg1<span class="token punctuation">.</span>c <span class="token operator">=</span> <span class="token boolean">nil</span> <span class="token punctuation">}</span> gp<span class="token punctuation">.</span>waiting <span class="token operator">=</span> <span class="token boolean">nil</span> <span class="token keyword">for</span> <span class="token boolean">_</span><span class="token punctuation">,</span> casei <span class="token operator">:=</span> <span class="token keyword">range</span> lockorder <span class="token punctuation">{</span> k <span class="token operator">=</span> <span class="token operator">&</span>scases<span class="token punctuation">[</span>casei<span class="token punctuation">]</span> <span class="token keyword">if</span> k<span class="token punctuation">.</span>kind <span class="token operator">==</span> caseNil <span class="token punctuation">{</span> <span class="token keyword">continue</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> sglist<span class="token punctuation">.</span>releasetime <span class="token operator">></span> <span class="token number">0</span> <span class="token punctuation">{</span> k<span class="token punctuation">.</span>releasetime <span class="token operator">=</span> sglist<span class="token punctuation">.</span>releasetime <span class="token punctuation">}</span> <span class="token keyword">if</span> sg <span class="token operator">==</span> sglist <span class="token punctuation">{</span> <span class="token comment">// sg has already been dequeued by the G that woke us up.</span> casi <span class="token operator">=</span> <span class="token function">int</span><span class="token punctuation">(</span>casei<span class="token punctuation">)</span> cas <span class="token operator">=</span> k <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> c <span class="token operator">=</span> k<span class="token punctuation">.</span>c <span class="token keyword">if</span> k<span class="token punctuation">.</span>kind <span class="token operator">==</span> caseSend <span class="token punctuation">{</span> c<span class="token punctuation">.</span>sendq<span class="token punctuation">.</span><span class="token function">dequeueSudoG</span><span class="token punctuation">(</span>sglist<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> c<span class="token punctuation">.</span>recvq<span class="token punctuation">.</span><span class="token function">dequeueSudoG</span><span class="token punctuation">(</span>sglist<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> sgnext <span class="token operator">=</span> sglist<span class="token punctuation">.</span>waitlink sglist<span class="token punctuation">.</span>waitlink <span class="token operator">=</span> <span class="token boolean">nil</span> <span class="token comment">//释放所有的锁</span> <span class="token function">releaseSudog</span><span class="token punctuation">(</span>sglist<span class="token punctuation">)</span> sglist <span class="token operator">=</span> sgnext <span class="token punctuation">}</span> <span class="token comment">//没找到case,重新循环</span> <span class="token keyword">if</span> cas <span class="token operator">==</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span> <span class="token keyword">goto</span> loop <span class="token punctuation">}</span> c <span class="token operator">=</span> cas<span class="token punctuation">.</span>c <span class="token keyword">if</span> debugSelect <span class="token punctuation">{</span> <span class="token function">print</span><span class="token punctuation">(</span><span class="token string">"wait-return: cas0="</span><span class="token punctuation">,</span> cas0<span class="token punctuation">,</span> <span class="token string">" c="</span><span class="token punctuation">,</span> c<span class="token punctuation">,</span> <span class="token string">" cas="</span><span class="token punctuation">,</span> cas<span class="token punctuation">,</span> <span class="token string">" kind="</span><span class="token punctuation">,</span> cas<span class="token punctuation">.</span>kind<span class="token punctuation">,</span> <span class="token string">"\n"</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> cas<span class="token punctuation">.</span>kind <span class="token operator">==</span> caseRecv <span class="token punctuation">{</span> recvOK <span class="token operator">=</span> <span class="token boolean">true</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> raceenabled <span class="token punctuation">{</span> <span class="token keyword">if</span> cas<span class="token punctuation">.</span>kind <span class="token operator">==</span> caseRecv <span class="token operator">&&</span> cas<span class="token punctuation">.</span>elem <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span> <span class="token function">raceWriteObjectPC</span><span class="token punctuation">(</span>c<span class="token punctuation">.</span>elemtype<span class="token punctuation">,</span> cas<span class="token punctuation">.</span>elem<span class="token punctuation">,</span> cas<span class="token punctuation">.</span>pc<span class="token punctuation">,</span> chanrecvpc<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> cas<span class="token punctuation">.</span>kind <span class="token operator">==</span> caseSend <span class="token punctuation">{</span> <span class="token function">raceReadObjectPC</span><span class="token punctuation">(</span>c<span class="token punctuation">.</span>elemtype<span class="token punctuation">,</span> cas<span class="token punctuation">.</span>elem<span class="token punctuation">,</span> cas<span class="token punctuation">.</span>pc<span class="token punctuation">,</span> chansendpc<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> msanenabled <span class="token punctuation">{</span> <span class="token keyword">if</span> cas<span class="token punctuation">.</span>kind <span class="token operator">==</span> caseRecv <span class="token operator">&&</span> cas<span class="token punctuation">.</span>elem <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span> <span class="token function">msanwrite</span><span class="token punctuation">(</span>cas<span class="token punctuation">.</span>elem<span class="token punctuation">,</span> c<span class="token punctuation">.</span>elemtype<span class="token punctuation">.</span>size<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> cas<span class="token punctuation">.</span>kind <span class="token operator">==</span> caseSend <span class="token punctuation">{</span> <span class="token function">msanread</span><span class="token punctuation">(</span>cas<span class="token punctuation">.</span>elem<span class="token punctuation">,</span> c<span class="token punctuation">.</span>elemtype<span class="token punctuation">.</span>size<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token function">selunlock</span><span class="token punctuation">(</span>scases<span class="token punctuation">,</span> lockorder<span class="token punctuation">)</span> <span class="token keyword">goto</span> retc bufrecv<span class="token punctuation">:</span> <span class="token comment">// 可以从缓冲区接收</span> <span class="token keyword">if</span> raceenabled <span class="token punctuation">{</span> <span class="token keyword">if</span> cas<span class="token punctuation">.</span>elem <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span> <span class="token function">raceWriteObjectPC</span><span class="token punctuation">(</span>c<span class="token punctuation">.</span>elemtype<span class="token punctuation">,</span> cas<span class="token punctuation">.</span>elem<span class="token punctuation">,</span> cas<span class="token punctuation">.</span>pc<span class="token punctuation">,</span> chanrecvpc<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token function">raceacquire</span><span class="token punctuation">(</span><span class="token function">chanbuf</span><span class="token punctuation">(</span>c<span class="token punctuation">,</span> c<span class="token punctuation">.</span>recvx<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token function">racerelease</span><span class="token punctuation">(</span><span class="token function">chanbuf</span><span class="token punctuation">(</span>c<span class="token punctuation">,</span> c<span class="token punctuation">.</span>recvx<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> msanenabled <span class="token operator">&&</span> cas<span class="token punctuation">.</span>elem <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span> <span class="token function">msanwrite</span><span class="token punctuation">(</span>cas<span class="token punctuation">.</span>elem<span class="token punctuation">,</span> c<span class="token punctuation">.</span>elemtype<span class="token punctuation">.</span>size<span class="token punctuation">)</span> <span class="token punctuation">}</span> recvOK <span class="token operator">=</span> <span class="token boolean">true</span> qp <span class="token operator">=</span> <span class="token function">chanbuf</span><span class="token punctuation">(</span>c<span class="token punctuation">,</span> c<span class="token punctuation">.</span>recvx<span class="token punctuation">)</span> <span class="token keyword">if</span> cas<span class="token punctuation">.</span>elem <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span> <span class="token comment">// 将chan缓存中的数据拷贝到 case.elem。 eg: a := <-ch, a就是case.elem</span> <span class="token function">typedmemmove</span><span class="token punctuation">(</span>c<span class="token punctuation">.</span>elemtype<span class="token punctuation">,</span> cas<span class="token punctuation">.</span>elem<span class="token punctuation">,</span> qp<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token function">typedmemclr</span><span class="token punctuation">(</span>c<span class="token punctuation">.</span>elemtype<span class="token punctuation">,</span> qp<span class="token punctuation">)</span> c<span class="token punctuation">.</span>recvx<span class="token operator"> </span> <span class="token keyword">if</span> c<span class="token punctuation">.</span>recvx <span class="token operator">==</span> c<span class="token punctuation">.</span>dataqsiz <span class="token punctuation">{</span> c<span class="token punctuation">.</span>recvx <span class="token operator">=</span> <span class="token number">0</span> <span class="token punctuation">}</span> c<span class="token punctuation">.</span>qcount<span class="token operator">--</span> <span class="token function">selunlock</span><span class="token punctuation">(</span>scases<span class="token punctuation">,</span> lockorder<span class="token punctuation">)</span> <span class="token keyword">goto</span> retc bufsend<span class="token punctuation">:</span> <span class="token comment">//可以发送到缓冲区</span> <span class="token keyword">if</span> raceenabled <span class="token punctuation">{</span> <span class="token function">raceacquire</span><span class="token punctuation">(</span><span class="token function">chanbuf</span><span class="token punctuation">(</span>c<span class="token punctuation">,</span> c<span class="token punctuation">.</span>sendx<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token function">racerelease</span><span class="token punctuation">(</span><span class="token function">chanbuf</span><span class="token punctuation">(</span>c<span class="token punctuation">,</span> c<span class="token punctuation">.</span>sendx<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token function">raceReadObjectPC</span><span class="token punctuation">(</span>c<span class="token punctuation">.</span>elemtype<span class="token punctuation">,</span> cas<span class="token punctuation">.</span>elem<span class="token punctuation">,</span> cas<span class="token punctuation">.</span>pc<span class="token punctuation">,</span> chansendpc<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> msanenabled <span class="token punctuation">{</span> <span class="token function">msanread</span><span class="token punctuation">(</span>cas<span class="token punctuation">.</span>elem<span class="token punctuation">,</span> c<span class="token punctuation">.</span>elemtype<span class="token punctuation">.</span>size<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token comment">// 将cas.elem拷贝到chan的缓存中。eg: ch <- a, a 就是 cas.elem</span> <span class="token function">typedmemmove</span><span class="token punctuation">(</span>c<span class="token punctuation">.</span>elemtype<span class="token punctuation">,</span> <span class="token function">chanbuf</span><span class="token punctuation">(</span>c<span class="token punctuation">,</span> c<span class="token punctuation">.</span>sendx<span class="token punctuation">)</span><span class="token punctuation">,</span> cas<span class="token punctuation">.</span>elem<span class="token punctuation">)</span> c<span class="token punctuation">.</span>sendx<span class="token operator"> </span> <span class="token keyword">if</span> c<span class="token punctuation">.</span>sendx <span class="token operator">==</span> c<span class="token punctuation">.</span>dataqsiz <span class="token punctuation">{</span> c<span class="token punctuation">.</span>sendx <span class="token operator">=</span> <span class="token number">0</span> <span class="token punctuation">}</span> c<span class="token punctuation">.</span>qcount<span class="token operator"> </span> <span class="token function">selunlock</span><span class="token punctuation">(</span>scases<span class="token punctuation">,</span> lockorder<span class="token punctuation">)</span> <span class="token keyword">goto</span> retc recv<span class="token punctuation">:</span> <span class="token comment">//可以从休眠的发件人(sg)接收</span> <span class="token function">recv</span><span class="token punctuation">(</span>c<span class="token punctuation">,</span> sg<span class="token punctuation">,</span> cas<span class="token punctuation">.</span>elem<span class="token punctuation">,</span> <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">selunlock</span><span class="token punctuation">(</span>scases<span class="token punctuation">,</span> lockorder<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span> <span class="token keyword">if</span> debugSelect <span class="token punctuation">{</span> <span class="token function">print</span><span class="token punctuation">(</span><span class="token string">"syncrecv: cas0="</span><span class="token punctuation">,</span> cas0<span class="token punctuation">,</span> <span class="token string">" c="</span><span class="token punctuation">,</span> c<span class="token punctuation">,</span> <span class="token string">"\n"</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> recvOK <span class="token operator">=</span> <span class="token boolean">true</span> <span class="token keyword">goto</span> retc rclose<span class="token punctuation">:</span> <span class="token comment">//在封闭channel的末尾读取</span> <span class="token function">selunlock</span><span class="token punctuation">(</span>scases<span class="token punctuation">,</span> lockorder<span class="token punctuation">)</span> recvOK <span class="token operator">=</span> <span class="token boolean">false</span> <span class="token keyword">if</span> cas<span class="token punctuation">.</span>elem <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span> <span class="token function">typedmemclr</span><span class="token punctuation">(</span>c<span class="token punctuation">.</span>elemtype<span class="token punctuation">,</span> cas<span class="token punctuation">.</span>elem<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> raceenabled <span class="token punctuation">{</span> <span class="token function">raceacquire</span><span class="token punctuation">(</span>c<span class="token punctuation">.</span><span class="token function">raceaddr</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">goto</span> retc send<span class="token punctuation">:</span> <span class="token comment">//可以发送到休眠的接收器(sg)</span> <span class="token keyword">if</span> raceenabled <span class="token punctuation">{</span> <span class="token function">raceReadObjectPC</span><span class="token punctuation">(</span>c<span class="token punctuation">.</span>elemtype<span class="token punctuation">,</span> cas<span class="token punctuation">.</span>elem<span class="token punctuation">,</span> cas<span class="token punctuation">.</span>pc<span class="token punctuation">,</span> chansendpc<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> msanenabled <span class="token punctuation">{</span> <span class="token function">msanread</span><span class="token punctuation">(</span>cas<span class="token punctuation">.</span>elem<span class="token punctuation">,</span> c<span class="token punctuation">.</span>elemtype<span class="token punctuation">.</span>size<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token function">send</span><span class="token punctuation">(</span>c<span class="token punctuation">,</span> sg<span class="token punctuation">,</span> cas<span class="token punctuation">.</span>elem<span class="token punctuation">,</span> <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">selunlock</span><span class="token punctuation">(</span>scases<span class="token punctuation">,</span> lockorder<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span> <span class="token keyword">if</span> debugSelect <span class="token punctuation">{</span> <span class="token function">print</span><span class="token punctuation">(</span><span class="token string">"syncsend: cas0="</span><span class="token punctuation">,</span> cas0<span class="token punctuation">,</span> <span class="token string">" c="</span><span class="token punctuation">,</span> c<span class="token punctuation">,</span> <span class="token string">"\n"</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">goto</span> retc retc<span class="token punctuation">:</span> <span class="token keyword">if</span> cas<span class="token punctuation">.</span>releasetime <span class="token operator">></span> <span class="token number">0</span> <span class="token punctuation">{</span> <span class="token function">blockevent</span><span class="token punctuation">(</span>cas<span class="token punctuation">.</span>releasetime<span class="token operator">-</span>t0<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> casi<span class="token punctuation">,</span> recvOK sclose<span class="token punctuation">:</span> <span class="token comment">//在关闭的channel上发送</span> <span class="token function">selunlock</span><span class="token punctuation">(</span>scases<span class="token punctuation">,</span> lockorder<span class="token punctuation">)</span> <span class="token function">panic</span><span class="token punctuation">(</span><span class="token function">plainError</span><span class="token punctuation">(</span><span class="token string">"send on closed channel"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> </code></pre>

这个函数看起来很复杂,我们总结一下上面的过程:

<ol><li>初始化pollorder切片和lockorder切片,其中pollorder是存放所有case的,lockorder是存放所有channel</li><li>对pollorder进行重排序,打乱所有case的顺序</li><li>锁定scase语句中所有的channel</li><li>按照随机顺序检测pollorder中case对应的channel是否ready:
4.1 如果case可读,则读取channel中数据,解锁所有的channel,然后返回(case index, true)
4.2 如果case可写,则将数据写入channel,解锁所有的channel,然后返回(case index, false)
4.3 所有case都未ready,则解锁所有的channel,然后返回(default index, false)</li><li>所有case都未ready,且没有default语句
5.1 将当前协程加入到所有channel的等待队列
5.2 当将协程转入阻塞,等待被唤醒</li><li>唤醒后返回channel对应的case index
6.1 如果是读操作,解锁所有的channel,然后返回(case index, true)
6.2 如果是写操作,解锁所有的channel,然后返回(case index, false)</li></ol>

其中,很多涉及到channel代码部分可参考之前的文章: Golang中channel的实现原理

<h1>
总结</h1> <ul><li>select语句中除default外,各case执行顺序是随机的</li><li>select语句中如果没有default语句,则会阻塞等待任意一个case</li><li>select语句中读操作要判断是否成功读取,关闭的channel也可以读取</li><li>select语句中除default外,每个case只能操作一个channel,要么读要么写</li></ul> 到此这篇关于“Golang中select的实现原理”的文章就介绍到这了,更多文章或继续浏览下面的相关文章,希望大家以后多多支持JQ教程网!

您可能感兴趣的文章:
golang url 收集
golang的select实现原理剖析
golang中timer定时器实现原理
golang的协程原理
Golang协程原理(一)
go 协程
想系统学习GO语言(Golang
golang中的mmap使用
golang学习之--select--case 原理【转载】
Golang channel 使用指南

[关闭]
~ ~