教程集 www.jiaochengji.com
教程集 >  Golang编程  >  正文 goroutine原理分析

goroutine原理分析

发布时间:2021-12-10   编辑: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>

<h3>文章目录</h3> <ul><li><ul><li>进程和线程</li><li><ul><li>进程-——拥有资源的人</li><li>线程——真正干活的人</li></ul></li><li>多线程和多协程</li><li><ul><li>多线程——多个人干多件事</li><li>一个小案例</li><li>多协程——一个人干多件事</li><li>本质</li></ul></li><li>goroutine的原理</li><li>GM模型</li><li>GPM模型</li><li>关于goroutine底层的线程的数量</li><li><ul><li>测试程序一</li><li>测试程序二</li><li>测试程序三</li><li>结论</li></ul></li></ul></li></ul>

<h2>进程和线程</h2>

在讲解goroutine之前,先来熟悉一下进程和线程的概念,因为只有通过概念之前对比,才能更加理解这些概念。

<h3>进程-——拥有资源的人</h3>

计算机的使用,大都是以进程为单位来管理的,比如我打开电脑版微信,桌面启动一个微信程序,本质上计算机启动了一个为微信进程,打开浏览器、播放器等等类似,当然有的应用软件不只启动一个进程。

在windows下,可以通过任务管理器看到启动的进程;在linux下,可以通过ps -aux命令,看到所有的进程。

由此,可以大致了解到,所谓进程就是运行的程序

如果安装完QQ软件,你不运行QQ,那么QQ就是一堆静态的躺在硬盘上的文件,当你一旦点击运行,那么计算机便创建了一个QQ进程,这个时候,就可以输入账号和密码登录QQ了,这就是和QQ进程交互的过程。

到这里,可以知道,进程是CPU和内存有关系的,因为程序的运行需要CPU和内存,更专业的说法就是进程就是程序在内存中的镜像,因为只有把程序载入内存才能运行。

上面讲了那么多废话,无非就是引入进程的以下特征:

<ul><li>进程是计算机资源管理的基本单位,例如内存分配、描述符分配、环境变量等等。</li><li>进程之间相互独立,互不干扰。</li></ul>

由于第一点资源上的独立,第二点也就是自然成立了。

<h3>
线程——真正干活的人</h3>

由于进程从宏观的上就可以看到,所以容易理解,但是线程是比进程更小的单位,貌似就不容易那么理解了。世间万事都是如此,更微观的现象,了解的成本的就越高,了解的人就越少。首先,线程是由进程创建,并且可以创建多个线程,正确的说是,线程的创建、运行、销毁都是由进程控制。

第一点:进程的拥有的资源,所有线程的线程都可以”看见“,都可以访问到。

第二点:线程是CPU执行的基本单位。

一点不能理解,既然进程分配资源的基本单位,拥有资源,那么这些资源给谁用呢?就是线程,线程来使用这些资源,所有的线程都可以使用到。线程使用这些资源干嘛呢?干活。

这里需要补充一点,CPU每次只能执行一个线程,那么其他的线程只能排队等待,至于这个线程要”霸占“CPU多久,取决于系统的线程调度算法,一般是执行一定的时间就让出CPU,然后排队等待。

下面做一个简单的比喻:

进程就好比一个公司,拥有很多资源,包括办公室、电脑、食堂、班车等等,而且公司与公司之间相互独立,互不影响。那么线程就是公司里的员工,每天都要工作,每个人都是基本的人力单位,所有的员工都可以使用公司的资源。所以进程与进程的独立性很强,基本不受对方的影响。但是一个进程的所有线程,就不那么独立了,因为使用同一个进程的资源,有时候就产生”矛盾“了,你看一个公司的所有员工之前经常发生摩擦和争吵。

<h2>
多线程和多协程</h2> <h3>多线程——多个人干多件事</h3>

刚才说了,一个进程拥有多个线程,就好比一家公司有好多员工,每个员工的工作任务不同,有的人写代码、有的人设计UI、有的人负责运营、有的人负责人事,他们的工作基本上是并行的。我之所以说基本上是并行的,是因为有时候他们之间也需要相互等待,比如软件没有开发完,就不能让测试进行测试,更不能发布到线上。

整体而言,一个公司的人越多,做事情的速度越快。但是不是全然正确,因为《人月神话》,因为一件工作的粒度不能无限细分下去,举个极端的例子,一车砖头,10个人1小时搬完,请问10万个人搬需要多少小时?是1万分之小时吗?也就是0.36秒?算了吧,10万个人排队就超过1小时,这时候人多反而降低效率了。当然,我举的这个例子很极端,只是为了说明问题。

<h3>
一个小案例</h3>

之前做过这样一个需求:有一个目录下会生成大量的文件,需要及时转移到另外一个目录下,文件的大小2KB—2GB之间,我最开始的做法是配置多个线程来转移文件,因为多个线程读取同一个目录,所以必须采用互斥锁。

线程的具体做法是:

<pre><code class="lang-c hljs"><span class="token comment">//配置8个线程</span> <span class="token function">lock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> filelist <span class="token operator">=</span><span class="token function">GetFiles</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">//获取50个文件</span> <span class="token function">unlock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">Move</span><span class="token punctuation">(</span>filelist<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">//开始转移</span> </code></pre>

经过测试就发现一个问题,假如这某一个时候,生成的文件都比较大,都是2GB,那么文件大,文件的数量就少,只能少量线程可以搬运文件,有的线程都空闲着,根本无法发挥多线程的优势。

另一种情况时,生产的文件都很小,只有1KB那么大,但是生成的速度很快,几秒钟便可以生成近百万个文件,如果这个时候,每个线程每次只获取50个文件,那么线程就需要多次访问互斥锁,性能反而降低,怎么办?改进方法:每个线程每次获取1万个文件,性能可以提高不少。

这个案例就涉及到任务粒度划分的问题,第一种情况,每个文件2G,任务粒度很大,一个文件只能由一个线程来处理,多线程没啥优势。第二种情况,每个文件1KB,任务粒度太小,每个线程根本没有饱和,造成线程争夺资源损耗性能。

总结:

<ul><li>多线程的优势显而易见,可以同时执行多种任务。</li><li>多线程合作执行同一任务时,其执行效果和任务的粒度有关系。</li></ul><h3>
多协程——一个人干多件事</h3>

协程,是一个线程更小的单位,由线程创建,由于线程是CPU调度执行的最小单元,那么第一个结论就是:

一个线程里协程,是不能并发的,因此协程之间不用加锁。

一个人每天早上上班后,开始投入工作,认真写代码,此时主协程在工作,代码写了250行,突然上级让TA过去开会,这时候放下手头的工作,创建一个开会的协程,开始进入会议模式,记下100行会议纪要,突然TA的电话响了,停止会议,创建接电话的协程,进入接电话模式,5分钟后电话结束,接电话的协程结束,回到刚才的切换的协程,即开会的协程,然后继续接着100行会议纪要继续记录,等会议结束,会议协程结束,回到刚才切换的协程,即写代码的协程,继续接着250行代码继续写。

以上的过程,大概就是协程切换的过程,是一个人串行的干多件事,干完一件事,就回到上一件事继续接着干。

<h3>
本质</h3>

其实协程的切换就是函数栈帧的切换,以为线程的结构就是栈帧、程序指针、各种寄存器等等,CPU拿到这些东西就可以执行一个线程了。

思考:

一个线程应该至少包含哪些东西?

CPU是如何通过机器指令执行程序的。所谓的栈,只是空间,但是在机器指令中,都是地址,变量、函数都是地址。

<h2>
goroutine的原理</h2>

最近学习和使用golang已经有半年多了,对于一个C/C 程序员来说,golang只是一门语言而已,并没有什么神奇之处,正如侯捷所说:

<blockquote>

代码面前,了无秘密。

</blockquote>

一门编程语言,最终还是要依赖操作系统实现各种功能,你看golang对应每个操作系统,都有一个版本,windows的话,你就需要下载windows版本的golang ,linux系统就要下载linux版本的golang。

说到这里,就不得不说golang的一个重要功能,就是协程——goroutine,利用关键go就可以轻易的开启协程,编写并发程序,利用chan就可以实现协程之间的通信。

上面,我们说了,真正的协程是不能并发的,因为一个协程在线程内部,而线程又是CPU执行的最小单位。但是goroutine是天生可以并发的,在语言层面获得支持,所以golang的强大,其实是golang的运行时干了太多的事情。

<pre><code class="lang-go hljs"><span class="token keyword">package</span> main <span class="token keyword">import</span> <span class="token punctuation">(</span> <span class="token string">"fmt"</span> <span class="token string">"time"</span> <span class="token punctuation">)</span> <span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"hello world"</span><span class="token punctuation">)</span> time<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span>time<span class="token punctuation">.</span>Second <span class="token operator">*</span> <span class="token number">100</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token comment">// hello.go</span> </code></pre>

编译运行,查看这个进程的线程数量,执行:

<blockquote>

top -H -p <code>pidof hello</code>

</blockquote> <pre><code class="lang-shell hljs">54605 KentZhan 20 0 3100 1072 588 S 0.0 0.0 0:00.00 hello 54606 KentZhan 20 0 3100 1072 588 S 0.0 0.0 0:00.00 hello 54607 KentZhan 20 0 3100 1072 588 S 0.0 0.0 0:00.00 hello 54608 KentZhan 20 0 3100 1072 588 S 0.0 0.0 0:00.00 hello 54609 KentZhan 20 0 3100 1072 588 S 0.0 0.0 0:00.00 hello </code></pre>

结果发现,一个简单的hello.go运行后,竟然开启了5个线程(包括主线程),那么goroutine之所以能轻松并发,是这些线程的支持,这些线程来执行应用层goroutine的任务。

<h2>
GM模型</h2>

由上面可知,go的运行时启动多个线程来执行多个goroutine任务,最开始go的调度器是GM模型。

G:表示goroutine,应用层开启的任务。

M:表示golang运行时开启的线程(machine),刚才在我的机器看到的是5个hello线程,当然这些线程的个数和CPU的核数,以及当前的goroutine的数量有关系,和当前的goroutine的行为有一定的关系。

这样的话,多个G相当于是任务队列,多个M构成线程池,然后每个M取出一定量的任务来执行,看起来很完美,但是实际中存在一些问题。

<ul><li>任务队列需要加锁,参考多线程可知,加锁在一定的任务粒度下会损耗性能。</li><li>假如M上正在执行任务阻塞,比如调用系统调用,那么这个M上的其他任务得不到执行。我之前在想,当goroutine调用系统调用的时候,M不能把当前的G切出去吗?执行下一个G,等系统调用返回,再继续执行。通过读libco源码才知道,系统调用只能阻塞,只不过libco采用了hook,改写了read函数,在新的read函数里,先epoll,等有数据了,在调用系统调用。所以,系统调用只能阻塞并且等待返回结果。</li><li>M频繁地调用系统调用阻塞,就把自己其他任务传递给其他的M,造成的一定的性能损耗。</li></ul><h2>
GPM模型</h2>

于是在G和M之间引入P,

P:是M调度G的一个中间层,可以理解为是对CPU的抽象,因为它的个数是由CPU的核数确定,可以由runtime.GOMAXPROCS(num)指定,程序运行后不会再改变。

G要想到M上执行,必须先绑定一个P,然后P在M上执行,所以我说P是G和M的中间层,P的数量决定了,同时最多有几个G在执行,P数数量小于等于CPU的核数。P可以控制整个程序的并发程度。

由P来完成一部分M的任务,之前是M从任务队列取任务,现在是P从任务对列取任务,放到自己的本地队列,当M上执行的G阻塞时,P与M分离,这个阻塞的G仍然和M绑在一起继续阻塞等待系统调用返回。那么P就可以继续和其他的M结合,你看M和G就解耦了,解决了GM模型存在的第二和第三个问题。此时,M只执行任务,P只分发任务,解耦了之前的M执行任务,又要管理任务的耦合。

这时候,M面对的不是G了,M只需找到一个P去结合,然后执行P中的G。

<h2>
关于goroutine底层的线程的数量</h2> <h3>测试程序一</h3> <pre><code class="lang-go hljs"><span class="token keyword">package</span> main <span class="token keyword">import</span> <span class="token punctuation">(</span> <span class="token string">"fmt"</span> <span class="token string">"os"</span> <span class="token string">"time"</span> <span class="token punctuation">)</span> <span class="token keyword">func</span> <span class="token function">WriteFile</span><span class="token punctuation">(</span>num <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> file <span class="token operator">:=</span> fmt<span class="token punctuation">.</span><span class="token function">Sprintf</span><span class="token punctuation">(</span><span class="token string">"%d.txt"</span><span class="token punctuation">,</span> num<span class="token punctuation">)</span> fp<span class="token punctuation">,</span> err <span class="token operator">:=</span> os<span class="token punctuation">.</span><span class="token function">OpenFile</span><span class="token punctuation">(</span>file<span class="token punctuation">,</span> os<span class="token punctuation">.</span>O_CREATE<span class="token operator">|</span>os<span class="token punctuation">.</span>O_RDWR<span class="token punctuation">,</span> <span class="token number">0666</span><span class="token punctuation">)</span> <span class="token keyword">if</span> <span class="token boolean">nil</span> <span class="token operator">!=</span> err <span class="token punctuation">{</span> fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"openFile failed, err:%s\n"</span><span class="token punctuation">,</span> err<span class="token punctuation">.</span><span class="token function">Error</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token punctuation">}</span> data <span class="token operator">:=</span> <span class="token string">"Hello"</span> <span class="token keyword">for</span> <span class="token punctuation">{</span> fp<span class="token punctuation">.</span><span class="token function">Write</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token function">byte</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</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> <span class="token number">30</span><span class="token punctuation">;</span> i<span class="token operator"> </span> <span class="token punctuation">{</span> <span class="token keyword">go</span> <span class="token function">WriteFile</span><span class="token punctuation">(</span>i<span class="token punctuation">)</span> <span class="token punctuation">}</span> time<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span>time<span class="token punctuation">.</span>Second <span class="token operator">*</span> <span class="token number">60</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token comment">//writefile.go 启动30个协程不断地写文件。 </span> </code></pre>

测试结果如下:我的机器8核,64位系统,golang 1.11

<pre><code class="lang-shell hljs">Tasks: 384 total, 2 running, 382 sleeping, 0 stopped, 0 zombie Cpu<span class="token punctuation">(</span>s<span class="token punctuation">)</span>: 1.0%us, 0.8%sy, 0.0%ni, 98.2%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st Mem: 132118792k total, 126218820k used, 5899972k free, 2641128k buffers Swap: 32767996k total, 920348k used, 31847648k free, 105824260k cached PID USER PR NI VIRT RES SHR S %CPU %MEM TIME COMMAND 24480 KentZhan 20 0 5736 1684 604 S 640.4 0.0 0:38.15 writefile 44955 KentZhan 20 0 225m 11m 10m R 65.2 0.0 2:56.84 smbd 23999 KentZhan 20 0 20140 1816 1204 R 1.9 0.0 0:00.01 <span class="token function">top</span> </code></pre> <pre><code class="lang-shell hljs"><span class="token punctuation">[</span>KentZhang@LOCAL-192-168-97-2 ~<span class="token punctuation">]</span>$ pstree -p 24480<span class="token operator">|</span><span class="token function">wc</span> -l 34 </code></pre>

这个进程CPU占有率640%,一共启动34个线程,加上主线程就是35个,业务层代码启动了30个协程。

<h3>
测试程序二</h3> <pre><code class="lang-go hljs"><span class="token keyword">package</span> main <span class="token keyword">import</span> <span class="token string">"time"</span> <span class="token keyword">func</span> <span class="token function">sleep</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">for</span> <span class="token punctuation">{</span> time<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span><span class="token number">1</span> <span class="token operator">*</span> time<span class="token punctuation">.</span>Second<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</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> <span class="token number">30</span><span class="token punctuation">;</span> i<span class="token operator"> </span> <span class="token punctuation">{</span> <span class="token keyword">go</span> <span class="token function">sleep</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> time<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span><span class="token number">60</span> <span class="token operator">*</span> time<span class="token punctuation">.</span>Second<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token comment">// sleep.go 开启30个协程,每个线程不断sleep</span> </code></pre> <pre><code class="lang-shell hljs"><span class="token punctuation">[</span>KentZhang@LOCAL-192-168-97-2 ~<span class="token punctuation">]</span>$ pstree -p 28276 sleep<span class="token punctuation">(</span>28276<span class="token punctuation">)</span>─┬─<span class="token punctuation">{</span>sleep<span class="token punctuation">}</span><span class="token punctuation">(</span>28277<span class="token punctuation">)</span> ├─<span class="token punctuation">{</span>sleep<span class="token punctuation">}</span><span class="token punctuation">(</span>28278<span class="token punctuation">)</span> ├─<span class="token punctuation">{</span>sleep<span class="token punctuation">}</span><span class="token punctuation">(</span>28279<span class="token punctuation">)</span> ├─<span class="token punctuation">{</span>sleep<span class="token punctuation">}</span><span class="token punctuation">(</span>28280<span class="token punctuation">)</span> ├─<span class="token punctuation">{</span>sleep<span class="token punctuation">}</span><span class="token punctuation">(</span>28281<span class="token punctuation">)</span> ├─<span class="token punctuation">{</span>sleep<span class="token punctuation">}</span><span class="token punctuation">(</span>28282<span class="token punctuation">)</span> ├─<span class="token punctuation">{</span>sleep<span class="token punctuation">}</span><span class="token punctuation">(</span>28283<span class="token punctuation">)</span> ├─<span class="token punctuation">{</span>sleep<span class="token punctuation">}</span><span class="token punctuation">(</span>28284<span class="token punctuation">)</span> ├─<span class="token punctuation">{</span>sleep<span class="token punctuation">}</span><span class="token punctuation">(</span>28285<span class="token punctuation">)</span> ├─<span class="token punctuation">{</span>sleep<span class="token punctuation">}</span><span class="token punctuation">(</span>28286<span class="token punctuation">)</span> ├─<span class="token punctuation">{</span>sleep<span class="token punctuation">}</span><span class="token punctuation">(</span>28330<span class="token punctuation">)</span> └─<span class="token punctuation">{</span>sleep<span class="token punctuation">}</span><span class="token punctuation">(</span>28332<span class="token punctuation">)</span> <span class="token punctuation">[</span>KentZhang@LOCAL-192-168-97-2 ~<span class="token punctuation">]</span>$ pstree -p 28276<span class="token operator">|</span><span class="token function">wc</span> -l 12 </code></pre>

CPU占有率很少,因为斗都在休眠中,后台线程12个,加上主线程一共13个,这个程序也是启动30个协程。

<h3>
测试程序三</h3> <pre><code class="lang-go hljs"><span class="token keyword">package</span> main <span class="token keyword">import</span> <span class="token punctuation">(</span> <span class="token string">"fmt"</span> <span class="token string">"os"</span> <span class="token string">"time"</span> <span class="token punctuation">)</span> <span class="token keyword">func</span> <span class="token function">WriteFile</span><span class="token punctuation">(</span>num <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> file <span class="token operator">:=</span> fmt<span class="token punctuation">.</span><span class="token function">Sprintf</span><span class="token punctuation">(</span><span class="token string">"%d.txt"</span><span class="token punctuation">,</span> num<span class="token punctuation">)</span> fp<span class="token punctuation">,</span> err <span class="token operator">:=</span> os<span class="token punctuation">.</span><span class="token function">OpenFile</span><span class="token punctuation">(</span>file<span class="token punctuation">,</span> os<span class="token punctuation">.</span>O_CREATE<span class="token operator">|</span>os<span class="token punctuation">.</span>O_RDWR<span class="token punctuation">,</span> <span class="token number">0666</span><span class="token punctuation">)</span> <span class="token keyword">if</span> <span class="token boolean">nil</span> <span class="token operator">!=</span> err <span class="token punctuation">{</span> fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"openFile failed, err:%s\n"</span><span class="token punctuation">,</span> err<span class="token punctuation">.</span><span class="token function">Error</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token punctuation">}</span> data <span class="token operator">:=</span> <span class="token string">"Hello"</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">200000</span><span class="token punctuation">;</span> i<span class="token operator"> </span> <span class="token punctuation">{</span> fp<span class="token punctuation">.</span><span class="token function">Write</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token function">byte</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</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> <span class="token number">30</span><span class="token punctuation">;</span> i<span class="token operator"> </span> <span class="token punctuation">{</span> <span class="token keyword">go</span> <span class="token function">WriteFile</span><span class="token punctuation">(</span>i<span class="token punctuation">)</span> <span class="token punctuation">}</span> time<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span>time<span class="token punctuation">.</span>Second <span class="token operator">*</span> <span class="token number">20000</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token comment">//sleep.go 修改了写文件的次数为20万次,之后协程退出。</span> </code></pre>

但是30个协程任务执行完后,全部退出,主协程休眠中,但是底层34的个线程依然还在,没有销毁。

<h3>
结论</h3>

加上上面的hello.go,一共也就三个测试程序,按理说,我是得不到什么结论的,但是阅读了其他资料,在上自己的推论,可得到一下我自己的结论。

1、执行线程的数量是不定的,根据需要创建,我的机器上最少是6个。

2、协程越多,执行线程未必越多,取决于于协程是否忙碌,忙碌的协程越多,执行线程就越多。

3、执行线程根据任务繁忙程度来创建,任务执行完,这些线程依然还在,没有销毁。

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

您可能感兴趣的文章:
Goroutine的调度分析(一)
golang 深入浅出之 goroutine 理解
GO 语言之 Goroutine 原理解析
golang 切片截取 内存泄露_怎么看待Goroutine 泄露
goroutine 调度器
golang goroutine 通知_深入golang之---goroutine并发控制与通信
goroutine泄露:原理、场景、检测和防范
Goroutine是如何工作的
[Go 教程系列笔记] goroutine(协程)
Goroutine并发调度模型深度解析&amp;手撸一个协程池

[关闭]
~ ~