<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <id>http://lernfunk.de/media/654321</id>
  <title>double12gzh - Posts in GoLang</title>
  <updated>2020-11-08T14:11:58.666193+00:00</updated>
  <link href="double12gzhblogs.readthedocs.io"/>
  <link href="double12gzhblogs.readthedocs.io/blogs/category/golang/atom.xml"/>
  <generator uri="https://ablog.readthedocs.org" version="0.10.9">ABlog</generator>
  <entry>
    <id>double12gzhblogs.readthedocs.io/blogs/GoLang/2020-09-20-%E5%B7%A7%E5%A6%99%E4%BD%BF%E7%94%A8WaitGroup%E5%A4%84%E7%90%86%E9%94%99%E8%AF%AF.html</id>
    <title>巧妙使用WaitGroupt处理错误</title>
    <updated>2020-09-20T00:00:00+00:00</updated>
    <author>
      <name>JeffreyGuan</name>
    </author>
    <content type="html">&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-20-handle-errors-with-waitgroup.png" /&gt;
&lt;/div&gt;
&lt;div class="section" id="waitgroupt"&gt;

&lt;div class="section" id="id1"&gt;
&lt;h2&gt;1. 写在前面&lt;/h2&gt;
&lt;p&gt;使用Go的众多好处之一是它在并发方面十分简单，而大家比熟悉的&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;WaitGroups&lt;/span&gt;&lt;/code&gt;就是一个很好的例子。虽然在并发处理上十分的方便，但要想有效地处理并发和错误可能很棘手。&lt;/p&gt;
&lt;p&gt;本篇文章旨在概述如何在不停止程序执行的情况下，运行多个goroutine并有效处理任何错误。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id2"&gt;
&lt;h2&gt;2. 具体实现&lt;/h2&gt;
&lt;p&gt;对于这个如何上，可以简单的概括为以下三点：&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;两个channel。这两个channel的作用是用于&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;传递错误&lt;/span&gt;&lt;/code&gt;和&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;传递WaitGroup何时完成&lt;/span&gt;&lt;/code&gt;。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;一个groutine。主要作用是用于监听&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;WaitGroup&lt;/span&gt;&lt;/code&gt;是否完成，如果完成了，将会关闭某个channel。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;一个Select。它用于监听出现的错误或&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;WaitGroup&lt;/span&gt;&lt;/code&gt;完成与否，无论谁先结束，那么Select就会先执行谁。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;具体代码如下：&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="main-go"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;main.go&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt; &lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt;

 &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="s"&gt;&amp;quot;errors&amp;quot;&lt;/span&gt;
     &lt;span class="s"&gt;&amp;quot;fmt&amp;quot;&lt;/span&gt;
     &lt;span class="s"&gt;&amp;quot;sync&amp;quot;&lt;/span&gt;
 &lt;span class="p"&gt;)&lt;/span&gt;

 &lt;span class="c1"&gt;// ErrorHandler 返回一个错误。&lt;/span&gt;
 &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nx"&gt;ErrorHandler&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;generated errors&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="c1"&gt;// 创建两个channel，一个用于传递错误，另一个表示WaitGroup是否结束。&lt;/span&gt;
     &lt;span class="nx"&gt;errCh&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="nx"&gt;wgCh&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

     &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;wg&lt;/span&gt; &lt;span class="nx"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WaitGroup&lt;/span&gt;

     &lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

     &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;WaitGroup 1st.&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

         &lt;span class="c1"&gt;// 这里可以定义我们需要执行的操作&lt;/span&gt;

         &lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
     &lt;span class="p"&gt;}()&lt;/span&gt;

     &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;WaitGroup 2nd&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

         &lt;span class="c1"&gt;// 返回自定义的错误&lt;/span&gt;
         &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;ErrorHandler&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
             &lt;span class="nx"&gt;errCh&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;
         &lt;span class="p"&gt;}&lt;/span&gt;

         &lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
     &lt;span class="p"&gt;}()&lt;/span&gt;

     &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Wait&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
         &lt;span class="nb"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wgCh&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="p"&gt;}()&lt;/span&gt;

     &lt;span class="c1"&gt;// 当有错误返回或WaitGroup执行结束时会被执行。&lt;/span&gt;
     &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="nx"&gt;wgCh&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
         &lt;span class="k"&gt;break&lt;/span&gt;
     &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="nx"&gt;errCh&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
         &lt;span class="nb"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;errCh&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;

     &lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Main func ended!&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;运行上述代码，我们可以得到以下的输出，也从而能验证我们已经基于此达到了我们的目的。&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="output"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-sh notranslate"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt; PS C:&lt;span class="se"&gt;\U&lt;/span&gt;sers&lt;span class="se"&gt;\j&lt;/span&gt;effrey&lt;span class="se"&gt;\D&lt;/span&gt;esktop&lt;span class="se"&gt;\h&lt;/span&gt;ello&amp;gt; go run main.go
 WaitGroup 1st.
 WaitGroup 2nd
 panic: generated errors.

 goroutine &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;running&lt;span class="o"&gt;]&lt;/span&gt;:
 main.main&lt;span class="o"&gt;()&lt;/span&gt;
         C:/Users/jeffrey/Desktop/hello/main.go:53 +0x2a6
 &lt;span class="nb"&gt;exit&lt;/span&gt; status &lt;span class="m"&gt;2&lt;/span&gt;
 PS C:&lt;span class="se"&gt;\U&lt;/span&gt;sers&lt;span class="se"&gt;\j&lt;/span&gt;effrey&lt;span class="se"&gt;\D&lt;/span&gt;esktop&lt;span class="se"&gt;\h&lt;/span&gt;ello&amp;gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;hr class="docutils" /&gt;
&lt;p&gt;欢迎关注我的微信公众号[double12gzh]：&lt;/p&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/wechat_public.jpg" /&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
</content>
    <link href="double12gzhblogs.readthedocs.io/blogs/GoLang/2020-09-20-%E5%B7%A7%E5%A6%99%E4%BD%BF%E7%94%A8WaitGroup%E5%A4%84%E7%90%86%E9%94%99%E8%AF%AF.html" rel="alternate"/>
    <published>2020-09-20T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>double12gzhblogs.readthedocs.io/blogs/GoLang/2020-09-21-goroutine%E5%8F%91%E7%94%9F%E5%88%87%E6%8D%A2%E6%97%B6%E8%83%8C%E5%90%8E%E5%8F%91%E7%94%9F%E4%BA%86%E4%BB%80%E4%B9%88.html</id>
    <title>goroutine切换背后那些事儿</title>
    <updated>2020-09-21T00:00:00+00:00</updated>
    <author>
      <name>大海星</name>
    </author>
    <content type="html">&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-21-go-switch/0.png" /&gt;
&lt;/div&gt;
&lt;div class="section" id="goroutine"&gt;

&lt;p&gt;本文基于于GoLang 1.13。&lt;/p&gt;
&lt;div class="section" id="id1"&gt;
&lt;h2&gt;1. 写在前面&lt;/h2&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;微信公众号：&lt;strong&gt;[double12gzh]&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;个人主页: &lt;a class="reference external" href="https://gzh.readthedocs.io"&gt;https://gzh.readthedocs.io&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;关注容器技术、关注&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Kubernetes&lt;/span&gt;&lt;/code&gt;。问题或建议，请公众号留言。&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;Goroutine很轻量，从资源消耗方面来看，它只需要一个2Kb的内存栈就可以运行；从运行时来看，它的运行成本也很低，将一个goroutine切换到另一个goroutine并不需要很多操作。&lt;/p&gt;
&lt;p&gt;在进行讲解golang的切换之前，我们先High
Level的看一下goroutine切换的相关内容。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id2"&gt;
&lt;h2&gt;2. 案例&lt;/h2&gt;
&lt;p&gt;golang会根据两种断点将goroutine调度到线程上：&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;当一个goroutine阻塞了。如：系统调用，mutex，或者通道。被阻塞的goroutine会进入睡眠模式/队列，让Go调度并运行一个等待的goroutine。被阻塞的goroutine进入睡眠模式/队列，允许Go调度&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/home/docs/checkouts/readthedocs.org/user_builds/gzh/checkouts/latest/blogs/GoLang/2020-09-21-goroutine发生切换时背后发生了什么.rst&lt;/span&gt;, line 36)&lt;/p&gt;
&lt;p&gt;Bullet list ends without a blank line; unexpected unindent.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;和运行一个等待的goroutine。&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;在函数调用过程中，假如goroutine必须增长它的栈。这个断点允许Go调度另一个goroutine，避免正在运行的那个goroutine占用CPU。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在这两种情况下，运行调度器的g0会用另一个准备运行的goroutine替换当前的goroutine。然后，被选中的goroutine取代g0，从而在线程上运行。&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;如果您想了解更多关于&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;g0&lt;/span&gt;&lt;/code&gt;的内容，请参考&lt;a class="reference external" href="https://www.cnblogs.com/double12gzh/p/13661777.html"&gt;g0&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;将一个运行中的goroutine切换到另一个运行中的goroutine涉及到两个切换。&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;g&lt;/span&gt;&lt;/code&gt;-&amp;gt; &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;g0&lt;/span&gt;&lt;/code&gt;。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-21-go-switch/1.png" /&gt;
&lt;/div&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;g0&lt;/span&gt;&lt;/code&gt;-&amp;gt; 另一个&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;g&lt;/span&gt;&lt;/code&gt;。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-21-go-switch/2.png" /&gt;
&lt;/div&gt;
&lt;p&gt;在GoLang中，groutine真的非常轻量。为了保存，它只需要两个东西：&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;goroutine是在哪一行停止的。即：在被调度前，goroutine是在哪一行停止的，当前要运行的指令被记录在程序计数器（PC）中。goroutine稍后将在同一点恢复。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;存放goroutine的堆栈。这个堆栈的目的是为了方便再次运行时恢复其局部变量。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;下面我们深入看一下。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="pc"&gt;
&lt;h2&gt;3. PC（程序记数器）&lt;/h2&gt;
&lt;p&gt;为了便于举例，我将使用一个通过channel进行通信的goroutine来说明，这两个goroutine中，一个可以产生数据的，其它的用于消费数据。代码如下：&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="main-go"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;main.go&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt; &lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt;

 &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="s"&gt;&amp;quot;fmt&amp;quot;&lt;/span&gt;
     &lt;span class="s"&gt;&amp;quot;sync&amp;quot;&lt;/span&gt;
 &lt;span class="p"&gt;)&lt;/span&gt;

 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;COUNT&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;

 &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;wg&lt;/span&gt; &lt;span class="nx"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WaitGroup&lt;/span&gt;

     &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

     &lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

     &lt;span class="c1"&gt;// 生产数据&lt;/span&gt;
     &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
             &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;
         &lt;span class="p"&gt;}&lt;/span&gt;

         &lt;span class="nb"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
     &lt;span class="p"&gt;}()&lt;/span&gt;

     &lt;span class="c1"&gt;// 消费数据&lt;/span&gt;
     &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

         &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
             &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                 &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                     &lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                 &lt;span class="p"&gt;}&lt;/span&gt;
             &lt;span class="p"&gt;}&lt;/span&gt;
         &lt;span class="p"&gt;}()&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;

     &lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Wait&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;消费者基本上会打印0到99的偶数，我们将重点关注第一个goroutine–生产者–向缓冲区添加数字。当缓冲区满了，它将在发送消息时阻塞。此时，Go要切换到g0，调度另一个goroutine。&lt;/p&gt;
&lt;p&gt;如前所述，Go首先需要保存当前指令，以便在同一指令处恢复goroutine。程序计数器(PC)保存在goroutine的内部结构中。&lt;/p&gt;
&lt;p&gt;上面的代码可以使用以下图来简单说明：&lt;/p&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-21-go-switch/3.png" /&gt;
&lt;/div&gt;
&lt;p&gt;指令和它们的地址可以通过命令获取：&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;➜  hello go tool compile -N -l main.go
➜  hello ls &lt;span class="p"&gt;|&lt;/span&gt; grep main.o
main.o
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;下面是生产者的一个示例：&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;➜  hello go tool objdump main.o
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-21-go-switch/4.png" /&gt;
&lt;/div&gt;
&lt;p&gt;在函数&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;runtime.chansend1&lt;/span&gt;&lt;/code&gt;上阻塞通道前，程序逐条指令执行。Go将当前的程序计数器保存到当前goroutine的内部属性中。在我们的例子中，Go保存程序计数器的地址是&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;0x4268d0&lt;/span&gt;&lt;/code&gt;，这
个地址是在&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;runtime&lt;/span&gt;&lt;/code&gt;和方法&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;runtime.chansend1&lt;/span&gt;&lt;/code&gt;内部的。&lt;/p&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-21-go-switch/5.png" /&gt;
&lt;/div&gt;
&lt;p&gt;然后，当&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;g0&lt;/span&gt;&lt;/code&gt;唤醒goroutine时，它将在同一指令处恢复，对数值进行循环并推入通道。&lt;/p&gt;
&lt;p&gt;下面我们来谈谈goroutine切换过程中的栈管理。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="stack"&gt;
&lt;h2&gt;4. 栈(stack)&lt;/h2&gt;
&lt;p&gt;在被阻塞之前，正在运行的goroutine有它的原始栈。这个堆栈包含临时内存，比如变量i:&lt;/p&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-21-go-switch/6.png" /&gt;
&lt;/div&gt;
&lt;p&gt;然后，当它在通道上阻塞时，goroutine将和它的堆栈一起切换到g0，这个goroutine将会有一个更大的栈。&lt;/p&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-21-go-switch/7.png" /&gt;
&lt;/div&gt;
&lt;p&gt;在切换之前，堆栈将被保存，以便在goroutine再次运行时恢复。&lt;/p&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-21-go-switch/8.png" /&gt;
&lt;/div&gt;
&lt;p&gt;我们现在已经完整地了解了goroutine切换中涉及的不同操作。现在让我们看看它是如何影响性能的。&lt;/p&gt;
&lt;p&gt;我们应该注意到，一些架构(比如arm)需要多保存一个寄存器LR(链接寄存器)。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id3"&gt;
&lt;h2&gt;5. 操作&lt;/h2&gt;
&lt;p&gt;为了测量goroutine切换可能需要的时间，我们将使用前面写的程序。然而，它并不能给出一个完美的性能视图，因为它可能取决于找到下一个要调度的goroutine所需的时间。这样goroutine的切换也会
影响性能，从函数prolog的切换比从通道上阻塞的goroutine切换要做的操作更多。&lt;/p&gt;
&lt;p&gt;我们来总结一下我们要测量的操作：&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;当前的g在通道上阻塞并切换到g0:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;PC和堆栈指针一起被保存在一个内部结构中&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;g0被设置为正在运行的goroutine。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;g0的堆栈取代了当前的堆栈。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;g0正在寻找一个新的goroutine来运行。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;g0必须与所选的goroutine进行切换。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;PC和堆栈指针被从内部结构中提取出来。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;程序跳转到获取的PC地址。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如下图：&lt;/p&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-21-go-switch/9.png" /&gt;
&lt;/div&gt;
&lt;p&gt;从&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;g&lt;/span&gt;&lt;/code&gt;到&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;g0&lt;/span&gt;&lt;/code&gt;或&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;g0到&lt;/span&gt;&lt;/code&gt;g的切换是最快的阶段。它们包含少量固定的指令，这一点与调度器检查许多源以寻找下一个要运行的goroutine的情况相反。根据运行程序的情况，这个阶段甚
至可能需要更多的时间。&lt;/p&gt;
&lt;p&gt;需要说明的一点是，对于以上测试的结果会因机器架构的不同而不同&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;p&gt;欢迎关注我的微信公众号：&lt;/p&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/wechat_public.jpg" /&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
</content>
    <link href="double12gzhblogs.readthedocs.io/blogs/GoLang/2020-09-21-goroutine%E5%8F%91%E7%94%9F%E5%88%87%E6%8D%A2%E6%97%B6%E8%83%8C%E5%90%8E%E5%8F%91%E7%94%9F%E4%BA%86%E4%BB%80%E4%B9%88.html" rel="alternate"/>
    <published>2020-09-21T00:00:00+00:00</published>
  </entry>
</feed>
