<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>面试题 on </title>
    <link>https://note.lican.site/tags/%E9%9D%A2%E8%AF%95%E9%A2%98/</link>
    <description>Recent content in 面试题 on </description>
    <generator>Hugo</generator>
    <language>en</language>
    <copyright>© lican.asia All rights reserved</copyright>
    <lastBuildDate>Fri, 31 Dec 2021 12:55:08 +0800</lastBuildDate>
    <atom:link href="https://note.lican.site/tags/%E9%9D%A2%E8%AF%95%E9%A2%98/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Go 什么时候会触发 GC？</title>
      <link>https://note.lican.site/posts/posts/go/when-gc/</link>
      <pubDate>Fri, 31 Dec 2021 12:55:08 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/when-gc/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;Go 语言作为一门新语言，在早期经常遭到唾弃的就是在垃圾回收（下称：GC）机制中 STW（Stop-The-World）的时间过长。&lt;/p&gt;&#xA;&lt;p&gt;那么这个时候，我们又会好奇一点，作为 STW 的起始，Go 语言中什么时候才会触发 GC 呢？&lt;/p&gt;&#xA;&lt;p&gt;今天就由煎鱼带大家一起来学习研讨一轮。&lt;/p&gt;&#xA;&lt;h2 id=&#34;什么是-gc&#34;&gt;什么是 GC&lt;/h2&gt;&#xA;&lt;p&gt;在计算机科学中，垃圾回收（GC）是一种自动管理内存的机制，垃圾回收器会去尝试回收程序不再使用的对象及其占用的内存。&lt;/p&gt;&#xA;&lt;p&gt;最早 John McCarthy 在 1959 年左右发明了垃圾回收，以简化 Lisp 中的手动内存管理的机制（来自 @wikipedia）。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/09425faf-f521-43a9-a7fa-1db3c5163914.png&#34; alt=&#34;图来自网络&#34;&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;为什么要-gc&#34;&gt;为什么要 GC&lt;/h2&gt;&#xA;&lt;p&gt;手动管理内存挺麻烦，管错或者管漏内存也很糟糕，将会直接导致程序不稳定（持续泄露）甚至直接崩溃。&lt;/p&gt;&#xA;&lt;h2 id=&#34;gc-触发场景&#34;&gt;GC 触发场景&lt;/h2&gt;&#xA;&lt;p&gt;GC 触发的场景主要分为两大类，分别是：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;系统触发：运行时自行根据内置的条件，检查、发现到，则进行 GC 处理，维护整个应用程序的可用性。&lt;/li&gt;&#xA;&lt;li&gt;手动触发：开发者在业务代码中自行调用 &lt;code&gt; runtime.GC&lt;/code&gt; 方法来触发 GC 行为。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h3 id=&#34;系统触发&#34;&gt;系统触发&lt;/h3&gt;&#xA;&lt;p&gt;在系统触发的场景中，Go 源码的 &lt;code&gt;src/runtime/mgc.go&lt;/code&gt; 文件，明确标识了 GC 系统触发的三种场景，分别如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;gcTriggerHeap&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;gcTriggerKind&lt;/span&gt; = &lt;span style=&#34;color:#66d9ef&#34;&gt;iota&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;gcTriggerTime&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;gcTriggerCycle&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;gcTriggerHeap：当所分配的堆大小达到阈值（由控制器计算的触发堆的大小）时，将会触发。&lt;/li&gt;&#xA;&lt;li&gt;gcTriggerTime：当距离上一个 GC 周期的时间超过一定时间时，将会触发。&#xA;-时间周期以 &lt;code&gt;runtime.forcegcperiod&lt;/code&gt; 变量为准，默认 2 分钟。&lt;/li&gt;&#xA;&lt;li&gt;gcTriggerCycle：如果没有开启 GC，则启动 GC。&#xA;&lt;ul&gt;&#xA;&lt;li&gt;在手动触发的 &lt;code&gt;runtime.GC&lt;/code&gt; 方法中涉及。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;手动触发&#34;&gt;手动触发&lt;/h3&gt;&#xA;&lt;p&gt;在手动触发的场景下，Go 语言中仅有 &lt;code&gt;runtime.GC&lt;/code&gt; 方法可以触发，也就没什么额外的分类的。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go map 如何缩容？</title>
      <link>https://note.lican.site/posts/posts/go/map-reset/</link>
      <pubDate>Fri, 31 Dec 2021 12:55:07 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/map-reset/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;前几天看到 Go 圈子的著名股神（不是我&amp;hellip;），在归类中简单的提到了 Go 语言中 map 的缩容的描述，这让我对其产生了兴趣，想要来一探究竟。&lt;/p&gt;&#xA;&lt;p&gt;我们常常喊扩缩容，扩缩容，但社区里都是清一色分析扩容机制，Go 面试官也都是卷 Go 语言 map 的扩容机制&amp;hellip;&lt;/p&gt;&#xA;&lt;p&gt;在 &lt;strong&gt;Go 语言中的 map 缩容机制是怎么做的&lt;/strong&gt;呢，今天就由煎鱼带大家一起研讨围观一轮。&lt;/p&gt;&#xA;&lt;h2 id=&#34;基本分析&#34;&gt;基本分析&lt;/h2&gt;&#xA;&lt;p&gt;在 Go 底层源码 src/runtime/map.go 中，扩缩容的处理方法是 grow 为前缀的方法来处理的。&lt;/p&gt;&#xA;&lt;p&gt;其中扩缩容涉及到的是插入元素的操作，对应 mapassign 方法：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;mapassign&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;maptype&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;h&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;hmap&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;unsafe&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Pointer&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;unsafe&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Pointer&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; !&lt;span style=&#34;color:#a6e22e&#34;&gt;h&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;growing&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;overLoadFactor&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;h&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;count&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;h&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;B&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;tooManyOverflowBuckets&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;h&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;noverflow&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;h&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;B&lt;/span&gt;)) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;hashGrow&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;h&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;goto&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;again&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;h&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;hmap&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;growing&lt;/span&gt;() &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;h&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;oldbuckets&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;overLoadFactor&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;count&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;B&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;uint8&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;count&lt;/span&gt; &amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;bucketCnt&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; uintptr(&lt;span style=&#34;color:#a6e22e&#34;&gt;count&lt;/span&gt;) &amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;loadFactorNum&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;bucketShift&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;B&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;loadFactorDen&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;tooManyOverflowBuckets&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;noverflow&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;uint16&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;B&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;uint8&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;B&lt;/span&gt; &amp;gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;15&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;B&lt;/span&gt; = &lt;span style=&#34;color:#ae81ff&#34;&gt;15&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;noverflow&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;=&lt;/span&gt; uint16(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;B&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;15&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;核心看到针对扩缩容的判断逻辑：&lt;/p&gt;</description>
    </item>
    <item>
      <title>面试官：为什么 Go 的负载因子是 6.5？</title>
      <link>https://note.lican.site/posts/posts/go/map-65/</link>
      <pubDate>Fri, 31 Dec 2021 12:55:07 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/map-65/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;最近我有一个朋友，在网上看到一个有趣的段子，引发了我一些兴趣。&lt;/p&gt;&#xA;&lt;p&gt;如下图：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/e81547d8-8de7-4310-a62f-e59ce4c0def2.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;听说是在最后的闲聊、吹水、聊人生、乱扯环节了，不是在技术环节了，所以大家也不用太在意什么技术评估法则（别杠）。&lt;/p&gt;&#xA;&lt;p&gt;煎鱼作为一名技术号主，看到这里的 6.5，就想给大家挖一挖，这到底是何物，和大家一同学习和增长知识！&lt;/p&gt;&#xA;&lt;h2 id=&#34;65-是什么&#34;&gt;6.5 是什么&lt;/h2&gt;&#xA;&lt;p&gt;实际上在 Go 语言中，就存在 6.5 这一概念，与 map 存在直接关系，因此我们需要先了解 map 的基本数据结构，再介绍 6.5 的背景和由来。&lt;/p&gt;&#xA;&lt;p&gt;开始学习 6.5 吧！&lt;/p&gt;&#xA;&lt;h3 id=&#34;了解-map-底层&#34;&gt;了解 map 底层&lt;/h3&gt;&#xA;&lt;p&gt;我以前在写《&lt;a href=&#34;https://eddycjy.com/posts/go/map/2019-03-05-map-access/&#34;&gt;深入理解 Go map：初始化和访问元素&lt;/a&gt;》时有介绍过 map 的基础数据结构。&lt;/p&gt;&#xA;&lt;p&gt;基本结构如下图：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/8679a84f-0d21-485d-b55f-dc9d70d5ddd1.png&#34; alt=&#34;map 基本数据结构&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;其中重要的一个基本单位是 hmap：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;hmap&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;count&lt;/span&gt;     &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;flags&lt;/span&gt;     &lt;span style=&#34;color:#66d9ef&#34;&gt;uint8&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;B&lt;/span&gt;         &lt;span style=&#34;color:#66d9ef&#34;&gt;uint8&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;noverflow&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;uint16&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;hash0&lt;/span&gt;     &lt;span style=&#34;color:#66d9ef&#34;&gt;uint32&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;buckets&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;unsafe&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Pointer&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;oldbuckets&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;unsafe&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Pointer&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;nevacuate&lt;/span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;uintptr&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;extra&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;mapextra&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;mapextra&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;overflow&lt;/span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;bmap&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;oldoverflow&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;bmap&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;nextOverflow&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;bmap&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;count：map 的大小，也就是 len() 的值，代指 map 中的键值对个数。&lt;/li&gt;&#xA;&lt;li&gt;flags：状态标识，主要是 goroutine 写入和扩容机制的相关状态控制。并发读写的判断条件之一就是该值。&lt;/li&gt;&#xA;&lt;li&gt;B：&lt;strong&gt;桶，最大可容纳的元素数量，值为 负载因子（默认 6.5） * 2 ^ B，是 2 的指数&lt;/strong&gt;。&lt;/li&gt;&#xA;&lt;li&gt;noverflow：溢出桶的数量。&lt;/li&gt;&#xA;&lt;li&gt;hash0：哈希因子。&lt;/li&gt;&#xA;&lt;li&gt;buckets：保存当前桶数据的指针地址（指向一段连续的内存地址，主要存储键值对数据）。&lt;/li&gt;&#xA;&lt;li&gt;oldbuckets，保存旧桶的指针地址。&lt;/li&gt;&#xA;&lt;li&gt;nevacuate：迁移进度。&lt;/li&gt;&#xA;&lt;li&gt;extra：原有 buckets 满载后，会发生扩容动作，在 Go 的机制中使用了增量扩容，如下为细项：&#xA;&lt;ul&gt;&#xA;&lt;li&gt;overflow 为 hmap.buckets （当前）溢出桶的指针地址。&lt;/li&gt;&#xA;&lt;li&gt;oldoverflow 为 hmap.oldbuckets （旧）溢出桶的指针地址。&lt;/li&gt;&#xA;&lt;li&gt;nextOverflow 为空闲溢出桶的指针地址。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;我们关注到 hmap 的 B 字段，其值就是 6.5，他就是我们在苦苦寻找的 6.5，但他又是什么呢？&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 并发读写 sync.map 的强大之处</title>
      <link>https://note.lican.site/posts/posts/go/sync-map/</link>
      <pubDate>Fri, 31 Dec 2021 12:54:50 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/sync-map/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;在之前的 《&lt;a href=&#34;http://mp.weixin.qq.com/s?__biz=MzUxMDI4MDc1NA==&amp;amp;mid=2247489045&amp;amp;idx=1&amp;amp;sn=197bda427246e16907c7b471a5dc0572&amp;amp;chksm=f9040348ce738a5ebf541954a4de29ce746238ab7f6e5a2af8a1765c5383ad4208f43b2bac4f&amp;amp;scene=21#wechat_redirect&#34;&gt;为什么 Go map 和 slice 是非线程安全的？&lt;/a&gt;》 文章中，我们讨论了 Go 语言的 map 和 slice 非线程安全的问题，基于此引申出了 map 的两种目前在业界使用的最多的并发支持的模式。&lt;/p&gt;&#xA;&lt;p&gt;分别是：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;原生 map + 互斥锁或读写锁 mutex。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;标准库 sync.Map（Go1.9及以后）。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;有了选择，总是有选择困难症的，这&lt;strong&gt;两种到底怎么选，谁的性能更加的好&lt;/strong&gt;？我有一个朋友说 标准库 sync.Map 性能菜的很，不要用。我到底听谁的&amp;hellip;&lt;/p&gt;&#xA;&lt;p&gt;今天煎鱼就带你揭秘 Go sync.map，我们先会了解清楚什么场景下，Go map 的多种类型怎么用，谁的性能最好！&lt;/p&gt;&#xA;&lt;p&gt;接着根据各 map 性能分析的结果，针对性的对 sync.map 进行源码解剖，了解 WHY。&lt;/p&gt;&#xA;&lt;p&gt;一起愉快地开始吸鱼之路。&lt;/p&gt;&#xA;&lt;h2 id=&#34;syncmap-优势&#34;&gt;sync.Map 优势&lt;/h2&gt;&#xA;&lt;p&gt;在 Go 官方文档中明确指出 Map 类型的一些建议：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6500ee41a08a415ca681fa427f5032b3~tplv-k3u1fbpfcp-zoom-1.image&#34; alt=&#34;图片&#34;&gt;&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;多个 goroutine 的并发使用是安全的，不需要额外的锁定或协调控制。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;大多数代码应该使用原生的 map，而不是单独的锁定或协调控制，以获得更好的类型安全性和维护性。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;同时 Map 类型，还针对以下场景进行了性能优化：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;当一个给定的键的条目只被写入一次但被多次读取时。例如在仅会增长的缓存中，就会有这种业务场景。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;当多个 goroutines 读取、写入和覆盖不相干的键集合的条目时。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;这两种情况与 Go map 搭配单独的 Mutex 或 RWMutex 相比较，使用 Map 类型可以大大减少锁的争夺。&lt;/p&gt;</description>
    </item>
    <item>
      <title>为什么 Go map 和 slice 是非线程安全的？</title>
      <link>https://note.lican.site/posts/posts/go/map-slice-concurrency/</link>
      <pubDate>Fri, 31 Dec 2021 12:54:49 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/map-slice-concurrency/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;初入 Go 语言的大门，有不少的小伙伴会快速的 3 天精通 Go，5 天上手项目，14 天上线业务迭代，21 天排查、定位问题，顺带捎个反省报告。&lt;/p&gt;&#xA;&lt;p&gt;其中最常见的初级错误，Go 面试较最爱问的问题之一：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8d1a0506617a4729abe35927d87508d0~tplv-k3u1fbpfcp-zoom-1.image&#34; alt=&#34;图片&#34;&gt;&#xA;（来自读者提问）&lt;/p&gt;&#xA;&lt;p&gt;为什么在 Go 语言里，map 和 slice 不支持并发读写，也就是是非线程安全的，为什么不支持？&lt;/p&gt;&#xA;&lt;p&gt;见招拆招后，紧接着就会开始讨论如何让他们俩 ”冤家“ 支持并发读写？&lt;/p&gt;&#xA;&lt;p&gt;今天我们这篇文章就来理一理，了解其前因后果，一起吸鱼学懂 Go 语言。&lt;/p&gt;&#xA;&lt;h2 id=&#34;非线程安全的例子&#34;&gt;非线程安全的例子&lt;/h2&gt;&#xA;&lt;h3 id=&#34;slice&#34;&gt;slice&lt;/h3&gt;&#xA;&lt;p&gt;我们使用多个 goroutine 对类型为 slice 的变量进行操作，看看结果会变的怎么样。&lt;/p&gt;&#xA;&lt;p&gt;如下：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func main() {&#xA; var s []string&#xA; for i := 0; i &amp;lt; 9999; i++ {&#xA;  go func() {&#xA;   s = append(s, &amp;#34;脑子进煎鱼了&amp;#34;)&#xA;  }()&#xA; }&#xA;&#xA; fmt.Printf(&amp;#34;进了 %d 只煎鱼&amp;#34;, len(s))&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出结果：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;// 第一次执行&#xA;进了 5790 只煎鱼&#xA;// 第二次执行&#xA;进了 7370 只煎鱼&#xA;// 第三次执行&#xA;进了 6792 只煎鱼&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;你会发现无论你执行多少次，每次输出的值大概率都不会一样。也就是追加进 slice 的值，出现了覆盖的情况。&lt;/p&gt;</description>
    </item>
    <item>
      <title>经典面试题：你觉得 Go 在什么时候会抢占 P？</title>
      <link>https://note.lican.site/posts/posts/go/gmp-why-p/</link>
      <pubDate>Thu, 24 Jun 2021 12:42:05 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/gmp-why-p/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;前几天我们有聊到《单核 CPU，开两个 Goroutine，其中一个死循环，会怎么样？》的问题，我们在一个细节部分有提到：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6d9cdfb09590496daeca3675aae08611~tplv-k3u1fbpfcp-zoom-1.image&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;有新的小伙伴会产生更多的疑问，那就是在 Go 语言中，是如何抢占 P 的呢，这里面是怎么做的？&lt;/p&gt;&#xA;&lt;p&gt;今天这篇文章我们就来解密抢占 P。&lt;/p&gt;&#xA;&lt;h2 id=&#34;调度器的发展史&#34;&gt;调度器的发展史&lt;/h2&gt;&#xA;&lt;p&gt;在 Go 语言中，Goroutine 早期是没有设计成抢占式的，早期 Goroutine 只有读写、主动让出、锁等操作时才会触发调度切换。&lt;/p&gt;&#xA;&lt;p&gt;这样有一个严重的问题，就是垃圾回收器进行 STW 时，如果有一个 Goroutine 一直都在阻塞调用，垃圾回收器就会一直等待他，不知道等到什么时候&amp;hellip;&lt;/p&gt;&#xA;&lt;p&gt;这种情况下就需要抢占式调度来解决问题。如果一个 Goroutine 运行时间过久，就需要进行抢占来解决。&lt;/p&gt;&#xA;&lt;p&gt;这块 Go 语言在 Go1.2 起开始实现抢占式调度器，不断完善直至今日：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Go0.x：基于单线程的程调度器。&lt;/li&gt;&#xA;&lt;li&gt;Go1.0：基于多线程的调度器。&lt;/li&gt;&#xA;&lt;li&gt;Go1.1：基于任务窃取的调度器。&lt;/li&gt;&#xA;&lt;li&gt;Go1.2 - Go1.13：基于协作的抢占式调度器。&lt;/li&gt;&#xA;&lt;li&gt;Go1.14：基于信号的抢占式调度器。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;调度器的新提案：非均匀存储器访问调度（Non-uniform memory access，NUMA），&#xA;但由于实现过于复杂，优先级也不够高，因此迟迟未提上日程。&lt;/p&gt;&#xA;&lt;p&gt;有兴趣的小伙伴可以详见 Dmitry Vyukov, dvyukov 所提出的 &lt;a href=&#34;https://docs.google.com/document/u/0/d/1d3iI2QWURgDIsSR6G2275vMeQ_X7w-qxM2Vp7iGwwuM/pub&#34;&gt;NUMA-aware scheduler for Go&lt;/a&gt;。&lt;/p&gt;&#xA;&lt;h2 id=&#34;为什么要抢占-p&#34;&gt;为什么要抢占 P&lt;/h2&gt;&#xA;&lt;p&gt;为什么会要想去抢占 P 呢，说白了就是不抢，就没机会运行，会 hang 死。又或是资源分配不均了，&lt;/p&gt;&#xA;&lt;p&gt;这在调度器设计中显然是不合理的。&lt;/p&gt;&#xA;&lt;p&gt;跟这个例子一样：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// Main Goroutine &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 模拟单核 CPU&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;runtime&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;GOMAXPROCS&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 模拟 Goroutine 死循环&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;go&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;time&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Sleep&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;time&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Millisecond&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;脑子进煎鱼了&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个例子在老版本的 Go 语言中，就会一直阻塞，没法重见天日，是一个需要做抢占的场景。&lt;/p&gt;</description>
    </item>
    <item>
      <title>跟面试官聊 Goroutine 泄露的 6 种方法，真刺激！</title>
      <link>https://note.lican.site/posts/posts/go/goroutine-leak/</link>
      <pubDate>Fri, 11 Jun 2021 12:54:49 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/goroutine-leak/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;前几天分享 Go 群友提问的文章时，有读者在朋友圈下提到，希望我能够针对 Goroutine 泄露这块进行讲解，他在面试的时候经常被问到。&lt;/p&gt;&#xA;&lt;p&gt;今天的男主角，就是 Go 语言的著名品牌标识 Goroutine，一个随随便便就能开几十万个快车进车道的大杀器。&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;go&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;() {}()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;本文会聚焦于 Goroutine 泄露的 N 种方法，进行详解和说明。&lt;/p&gt;&#xA;&lt;h2 id=&#34;为什么要问&#34;&gt;为什么要问&lt;/h2&gt;&#xA;&lt;p&gt;面试官为啥会问 Goroutine（协程）泄露这种奇特的问题呢？&lt;/p&gt;&#xA;&lt;p&gt;可以猜测是：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Goroutine 实在是使用门槛实在是太低了，随手就一个就能起，出现了不少滥用的情况。例如：并发 map。&lt;/li&gt;&#xA;&lt;li&gt;Goroutine 本身在 Go 语言的标准库、复合类型、底层源码中应用广泛。例如：HTTP Server 对每一个请求的处理就是一个协程去运行。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;很多 Go 工程在线上出事故时，基本 Goroutine 的关联，大家都会作为救火队长，风风火火的跑去看指标、看日志，通过 PProf 采集 Goroutine 运行情况等。&lt;/p&gt;&#xA;&lt;p&gt;自然他也就是最受瞩目的那颗 “星” 了，所以在日常面试中，被问几率也就极高了。&lt;/p&gt;&#xA;&lt;h2 id=&#34;goroutine-泄露&#34;&gt;Goroutine 泄露&lt;/h2&gt;&#xA;&lt;p&gt;了解清楚大家爱问的原因后，我们开始对 Goroutine 泄露的 N 种方法进行研究，希望通过前人留下的 “坑”，了解其原理和避开这些问题。&lt;/p&gt;&#xA;&lt;p&gt;泄露的原因大多集中在：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Goroutine 内正在进行 channel/mutex 等读写操作，但由于逻辑问题，某些情况下会被一直阻塞。&lt;/li&gt;&#xA;&lt;li&gt;Goroutine 内的业务逻辑进入死循环，资源一直无法释放。&lt;/li&gt;&#xA;&lt;li&gt;Goroutine 内的业务逻辑进入长时间等待，有不断新增的 Goroutine 进入等待。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;接下来我会引用在网上冲浪收集到的一些 Goroutine 泄露例子（会在文末参考注明出处）。&lt;/p&gt;&#xA;&lt;h3 id=&#34;channel-使用不当&#34;&gt;channel 使用不当&lt;/h3&gt;&#xA;&lt;p&gt;Goroutine+Channel 是最经典的组合，因此不少泄露都出现于此。&lt;/p&gt;</description>
    </item>
    <item>
      <title>你知道 Go 结构体和结构体指针调用有什么区别吗？</title>
      <link>https://note.lican.site/posts/posts/go/struct-pointer/</link>
      <pubDate>Sun, 06 Jun 2021 12:21:30 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/struct-pointer/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;前几天在分享《Go 结构体是否可以比较，为什么？》时，有小伙伴提出了新的问题：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://image.eddycjy.com/a4d34c5312339b9909e482c18f0cdf4a.png&#34; alt=&#34;来自文章评论区&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;虽然大家提问题的速度已经超出了本鱼写文章的速度&amp;hellip;不过作为宠粉狂鱼，在此刻清明假期时还是写下了这篇文章。&lt;/p&gt;&#xA;&lt;p&gt;我在网上冲浪时搜索了相关问题，发现 6 年前就有 Go 开发者有一模一样的疑问，真是困扰了一代又一代的小伙伴。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://image.eddycjy.com/dbe910917c86e103648e314f79896a81.png&#34; alt=&#34;来自 stackoverflow.com&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;本期的男主角是《&lt;strong&gt;Go 结构体和结构体指针调用有什么区别&lt;/strong&gt;》，希望对大家有所帮助，带来一些思考。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;请在此处默念自己心目中的答案&lt;/strong&gt;，再和煎鱼一同研讨一波 Go 的技术哲学。&lt;/p&gt;&#xA;&lt;h2 id=&#34;结构体是什么&#34;&gt;结构体是什么&lt;/h2&gt;&#xA;&lt;p&gt;在 Go 语言中有个基本类型，开发者们称之为结构体（struct）。是 Go 语言中非常常用的，基本定义：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;struct_variable_type&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;member&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;definition&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;member&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;definition&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;member&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;definition&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;简单示例：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;package&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Vertex&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Name1&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Name2&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Vertex&lt;/span&gt;{&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;脑子进了&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;煎鱼&amp;#34;&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Name2&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;蒸鱼&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Name2&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;输出结果：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;蒸鱼&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这部分属于基础知识，因此不再过多解释。如果看不懂，建议重学 Go 语言语法基础。&lt;/p&gt;&#xA;&lt;h2 id=&#34;结构体和指针调用&#34;&gt;结构体和指针调用&lt;/h2&gt;&#xA;&lt;p&gt;讲解前置概要后，直接进入本文主题。如下例子：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;MyStruct&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Name&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;MyStruct&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;SetName1&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Name&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;MyStruct&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;SetName2&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Name&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;该程序声明了一个 &lt;code&gt;User&lt;/code&gt; 结构体，其包含两个结构体方法，分别是 &lt;code&gt;SetName1&lt;/code&gt; 和 &lt;code&gt;SetName2&lt;/code&gt; 方法，两者之间的差异就是&lt;strong&gt;引用的方式不同&lt;/strong&gt;。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 面试官：单核 CPU，开两个 Goroutine，其中一个死循环，会怎么样？</title>
      <link>https://note.lican.site/posts/posts/go/go-tips-goroutineloop/</link>
      <pubDate>Mon, 05 Apr 2021 16:17:23 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/go-tips-goroutineloop/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;最近金三银四，是面试的季节。在我的 Go 读者交流群里出现了许多小伙伴在讨论自己面试过程中所遇到的一些 Go 面试题。&lt;/p&gt;&#xA;&lt;p&gt;今天的男主角，是与 Go 工程师有调度相关的知识，那就是 “&lt;strong&gt;单核 CPU，开两个 Goroutine，其中一个死循环，会怎么样？&lt;/strong&gt;”&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;请在此处默念自己心目中的答案&lt;/strong&gt;，再往和煎鱼一起研讨一波 Go 的技术哲学。&lt;/p&gt;&#xA;&lt;h2 id=&#34;问题定义&#34;&gt;问题定义&lt;/h2&gt;&#xA;&lt;p&gt;针对这个问题，我们需要把问题剖开来看看，其具有以下几个元素：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;运行 Go 程序的计算机只有一个单核 CPU。&lt;/li&gt;&#xA;&lt;li&gt;两个 Goroutine 在运行。&lt;/li&gt;&#xA;&lt;li&gt;一个 Goroutine 死循环。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;根据这道题的题意，可大致理解其想要问的是 Go 调度相关的一些知识理解。&lt;/p&gt;&#xA;&lt;h3 id=&#34;单核-cpu&#34;&gt;单核 CPU&lt;/h3&gt;&#xA;&lt;p&gt;第一个要点，就是要明确 “计算机只有一个单核 CPU” 这一个变量定义，对 Go 程序会产生什么影响，否则很难继续展开。&lt;/p&gt;&#xA;&lt;p&gt;既然明确涉及 Goroutine，这里就会考察到你对 Go 的调度模型 GMP 的基本理解了。&lt;/p&gt;&#xA;&lt;p&gt;从单核 CPU 来看，最大的影响就是 GMP 模型中的 P，因为 P 的数量默认是与 CPU 核数（GOMAXPROCS）保持一致的。&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;G：Goroutine，实际上我们每次调用 &lt;code&gt;go func&lt;/code&gt; 就是生成了一个 G。&lt;/li&gt;&#xA;&lt;li&gt;P：Processor，处理器，一般 P 的数量就是处理器的核数，可以通过 &lt;code&gt;GOMAXPROCS&lt;/code&gt; 进行修改。&lt;/li&gt;&#xA;&lt;li&gt;M：Machine，系统线程。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;这三者交互实际来源于 Go 的 M: N 调度模型。也就是 M 必须与 P 进行绑定，然后不断地在 M 上循环寻找可运行的 G 来执行相应的任务。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 内存泄露之痛，这篇把 Go timer.After 问题根因讲透了！</title>
      <link>https://note.lican.site/posts/posts/go/go-tips-timer-memory/</link>
      <pubDate>Mon, 05 Apr 2021 16:16:47 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/go-tips-timer-memory/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;前几天在公众号分享了一篇 Go timer 源码解析的文章《难以驾驭的 Go timer，一文带你参透计时器的奥秘》。&lt;/p&gt;&#xA;&lt;p&gt;在评论区有小伙伴提到了经典的 &lt;code&gt;timer.After&lt;/code&gt; 泄露问题，希望我能聊聊，这是一个不能不知的一个大 “坑”。&lt;/p&gt;&#xA;&lt;p&gt;今天这篇文章煎鱼就带大家来研讨一下这个问题。&lt;/p&gt;&#xA;&lt;h2 id=&#34;timerafter&#34;&gt;timer.After&lt;/h2&gt;&#xA;&lt;p&gt;今天是男主角是Go 标准库 time 所提供的 &lt;code&gt;After&lt;/code&gt; 方法。函数签名如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;After&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Duration&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;-&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;chan&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Time&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;该方法可以在一定时间（根据所传入的 Duration）后主动返回 &lt;code&gt;time.Time&lt;/code&gt; 类型的 channel 消息。&lt;/p&gt;&#xA;&lt;p&gt;在常见的场景下，我们会基于此方法做一些计时器相关的功能开发，例子如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;ch&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; make(&lt;span style=&#34;color:#66d9ef&#34;&gt;chan&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;go&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;time&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Sleep&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;time&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Second&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;ch&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;脑子进煎鱼了&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;select&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt; = &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;-&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;ch&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;-&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;time&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;After&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;time&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Second&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;煎鱼出去了，超时了！！！&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在运行 1 秒钟后，输出结果：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;煎鱼出去了，超时了！！！&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;上述程序在在运行 1 秒钟后将触发 &lt;code&gt;time.After&lt;/code&gt; 方法的定时消息返回，输出了超时的结果。&lt;/p&gt;&#xA;&lt;h2 id=&#34;坑在哪里&#34;&gt;坑在哪里&lt;/h2&gt;&#xA;&lt;p&gt;从例子来看似乎非常正常，也没什么 “坑” 的样子。难道是 &lt;code&gt;timer.After&lt;/code&gt; 方法的虚晃一枪？&lt;/p&gt;&#xA;&lt;p&gt;我们再看一个不像是有问题例子，这在 Go 工程中经常能看见，只是大家都没怎么关注。&lt;/p&gt;&#xA;&lt;p&gt;代码如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;ch&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; make(&lt;span style=&#34;color:#66d9ef&#34;&gt;chan&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;go&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;in&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;ch&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;in&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;select&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt; = &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;-&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;ch&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#75715e&#34;&gt;// do something...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;-&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;time&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;After&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;time&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Minute&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;现在是：%d，我脑子进煎鱼了！&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;time&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Now&lt;/span&gt;().&lt;span style=&#34;color:#a6e22e&#34;&gt;Unix&lt;/span&gt;())&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在上述代码中，我们构造了一个 &lt;code&gt;for+select+channel&lt;/code&gt; 的一个经典的处理模式。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 面试官：Go 结构体是否可以比较，为什么？</title>
      <link>https://note.lican.site/posts/posts/go/go-tips-sturct/</link>
      <pubDate>Mon, 05 Apr 2021 16:16:00 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/go-tips-sturct/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;最近金三银四，是面试的季节。在我的 Go 读者交流群里出现了许多小伙伴在讨论自己面试过程中所遇到的一些 Go 面试题。&lt;/p&gt;&#xA;&lt;p&gt;今天的男主角，是 Go 工程师的必修技能，也是极容易踩坑的地方，就是 “&lt;strong&gt;Go 面试题：Go 结构体（struct）是否可以比较？&lt;/strong&gt;”&lt;/p&gt;&#xA;&lt;p&gt;如果可以比较，是为什么？如果不可以比较，又是为什么？&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;请在此处默念自己心目中的答案&lt;/strong&gt;，再往和煎鱼一起研讨一波 Go 的技术哲学。&lt;/p&gt;&#xA;&lt;h2 id=&#34;结构体是什么&#34;&gt;结构体是什么&lt;/h2&gt;&#xA;&lt;p&gt;在 Go 语言中有个基本类型，开发者们称之为结构体（struct）。是 Go 语言中非常常用的，基本定义：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;struct_variable_type&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;member&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;definition&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;member&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;definition&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;member&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;definition&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;简单示例：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;package&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Vertex&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Name1&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Name2&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Vertex&lt;/span&gt;{&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;脑子进了&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;煎鱼&amp;#34;&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Name2&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;蒸鱼&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Name2&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;输出结果：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;蒸鱼&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这部分属于基础知识，因此不再过多解释。如果看不懂，建议重学 Go 语言语法基础。&lt;/p&gt;&#xA;&lt;h2 id=&#34;比较两下&#34;&gt;比较两下&lt;/h2&gt;&#xA;&lt;h3 id=&#34;例子一&#34;&gt;例子一&lt;/h3&gt;&#xA;&lt;p&gt;接下来正式开始研讨 Go 结构体比较的问题，第一个例子如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Value&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Name&lt;/span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Gender&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;v1&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Value&lt;/span&gt;{&lt;span style=&#34;color:#a6e22e&#34;&gt;Name&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;煎鱼&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;Gender&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;男&amp;#34;&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;v2&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Value&lt;/span&gt;{&lt;span style=&#34;color:#a6e22e&#34;&gt;Name&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;煎鱼&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;Gender&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;男&amp;#34;&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;v1&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;v2&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;脑子进煎鱼了&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;脑子没进煎鱼&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我们声明了两个变量，分别是 v1 和 v2。其都是 &lt;code&gt;Value&lt;/code&gt; 结构体的实例化，是同一个结构体的两个实例。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 面试官：GMP 模型，为什么要有 P？</title>
      <link>https://note.lican.site/posts/posts/go/go-tips-gmp-p/</link>
      <pubDate>Mon, 05 Apr 2021 16:15:20 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/go-tips-gmp-p/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;最近金三银四，是面试的季节。在我的 Go 读者交流群里出现了许多小伙伴在讨论自己面试过程中所遇到的一些 Go 面试题。&lt;/p&gt;&#xA;&lt;p&gt;今天的主角，是 Go 面试的万能题 GMP 模型的延伸题（疑问），那就是 ”&lt;strong&gt;GMP 模型，为什么要有 P&lt;/strong&gt;？“&lt;/p&gt;&#xA;&lt;p&gt;进一步推敲问题的背后，其实这个面试题本质是想问：”&lt;strong&gt;GMP 模型，为什么不是 G 和 M 直接绑定就完了，还要搞多一个 P 出来，那么麻烦，为的是什么，是要解决什么问题吗&lt;/strong&gt;？“&lt;/p&gt;&#xA;&lt;p&gt;这篇文章煎鱼就带你一同探索，GM、GMP 模型的变迁是因为什么原因。&lt;/p&gt;&#xA;&lt;h2 id=&#34;gm-模型&#34;&gt;GM 模型&lt;/h2&gt;&#xA;&lt;p&gt;在 Go1.1 之前 Go 的调度模型其实就是 GM 模型，也就是没有 P。&lt;/p&gt;&#xA;&lt;p&gt;今天带大家一起回顾过去的设计。&lt;/p&gt;&#xA;&lt;h3 id=&#34;解密-go10-源码&#34;&gt;解密 Go1.0 源码&lt;/h3&gt;&#xA;&lt;p&gt;我们了解一个东西的办法之一就是看源码，和煎鱼一起看看 Go1.0.1 的&lt;a href=&#34;https://github.com/golang/go/blob/go1.0.1/src/pkg/runtime/proc.c&#34;&gt;调度器源码&lt;/a&gt;的核心关键步骤：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;schedule&lt;/span&gt;(G &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;gp)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;schedlock&lt;/span&gt;();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt;(gp &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; nil) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;switch&lt;/span&gt;(gp&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;status){&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; Grunnable:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; Gdead:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#75715e&#34;&gt;// Shouldn&amp;#39;t have been running!&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;runtime&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;·&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;throw&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;bad gp-&amp;gt;status in sched&amp;#34;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; Grunning:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;gp&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;status &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Grunnable;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;gput&lt;/span&gt;(gp);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;break&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;gp &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;nextgandunlock&lt;/span&gt;();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;gp&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;readyonstop &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;gp&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;status &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Grunning;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;m&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;curg &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; gp;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;gp&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;m &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; m;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;runtime&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;·&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;gogo&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;gp&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;sched, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;调用 &lt;code&gt;schedlock&lt;/code&gt; 方法来获取全局锁。&lt;/li&gt;&#xA;&lt;li&gt;获取全局锁成功后，将当前 Goroutine 状态从 Running（正在被调度） 状态修改为 Runnable（可以被调度）状态。&lt;/li&gt;&#xA;&lt;li&gt;调用 &lt;code&gt;gput&lt;/code&gt; 方法来保存当前 Goroutine 的运行状态等信息，以便于后续的使用；&lt;/li&gt;&#xA;&lt;li&gt;调用 &lt;code&gt;nextgandunlock&lt;/code&gt; 方法来寻找下一个可运行 Goroutine，并且释放全局锁给其他调度使用。&lt;/li&gt;&#xA;&lt;li&gt;获取到下一个待运行的 Goroutine 后，将其的运行状态修改为 Running。&lt;/li&gt;&#xA;&lt;li&gt;调用 &lt;code&gt;runtime·gogo&lt;/code&gt; 方法，将刚刚所获取到的下一个待执行的 Goroutine 运行起来。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;思考-gm-模型&#34;&gt;思考 GM 模型&lt;/h3&gt;&#xA;&lt;p&gt;通过对 Go1.0.1 的调度器源码剖析，我们可以发现一个比较有趣的点。那就是调度器本身（schedule 方法），在正常流程下，是不会返回的，也就是不会结束主流程。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 群友提问：进程、线程都有 ID，为什么 Goroutine 没有 ID？</title>
      <link>https://note.lican.site/posts/posts/go/go-tips-goroutineid/</link>
      <pubDate>Mon, 05 Apr 2021 16:14:14 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/go-tips-goroutineid/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;最近金三银四，是面试的季节。在我的 Go 读者交流群里出现了许多小伙伴在讨论自己面试过程中所遇到的一些 Go 面试题。&lt;/p&gt;&#xA;&lt;p&gt;今天的主角，是大家在既有语言基础的情况下，学 Goroutine 时会容易纠结的一点。就是 “&lt;strong&gt;进程、线程都有 ID，为什么 Goroutine 没有 GoroutineID？&lt;/strong&gt;”。&lt;/p&gt;&#xA;&lt;p&gt;这是为什么呢，怎么做那些跨协程处理呢？&lt;/p&gt;&#xA;&lt;h2 id=&#34;goroutineid-是什么&#34;&gt;GoroutineID 是什么&lt;/h2&gt;&#xA;&lt;p&gt;我们要知道，为什么大家会下意识的想去要 GoroutineID，下面引用 Go 语言圣经中的表述：&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;在大多数支持多线程的操作系统和程序语言中，当前的线程都有一个独特的身份（ID），并且这个身份信息可以以一个普通值的形式被很容易地获取到，典型的可以是一个 integer 或者指针值。这种情况下我们做一个抽象化的 thread-local storage（线程本地存储，多线程编程中不希望其它线程访问的内容）就很容易，只需要以线程的 ID 作为 key 的一个 map 就可以解决问题，每一个线程以其 ID 就能从中获取到值，且和其它线程互不冲突。&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;也就在常规的进程、线程中都有其 ID 的概念，我们可以在程序中通过 ID 来获取其他进程、线程中的数据，甚至是传输数据。就像一把钥匙一样，有了他干啥都可以。&lt;/p&gt;&#xA;&lt;p&gt;GoroutineID 的概念也是类似的，也就是协程的 ID。我们下意识的就期望通过协程 ID 来进行跨协程的操作。&lt;/p&gt;&#xA;&lt;p&gt;但，在 Go 语言中 GoroutineID 并没有显式获取的办法，这就要打个大大的疑惑了。&lt;/p&gt;&#xA;&lt;h2 id=&#34;为什么没有-goroutineid&#34;&gt;为什么没有 GoroutineID&lt;/h2&gt;&#xA;&lt;p&gt;为什么在 Go 语言中没有 GoroutineID 呢，是从一开始就没有的，还是，这样子设计的原因是什么呢？&lt;/p&gt;&#xA;&lt;p&gt;其实 Go 语言在以前是有暴露方法去获取 GoroutineID 的，但在 Go1.4 后就把该方法给隐藏起来了，不建议大家使用。&lt;/p&gt;&#xA;&lt;p&gt;也就是明面上没有 GoroutineID，是一个有意而为之的行为。原因是：&lt;strong&gt;根据以往的经验，认为 thread-local storage 存在被滥用的可能性，且带来许多不必要的复杂度&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;p&gt;简单来讲，Andrew Gerrand 的回答是 ”&lt;strong&gt;thread-local storage 的成本远远超过了它们的收益。它们只是不适合 Go 语言&lt;/strong&gt;。”&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 面试官：Go interface 的一个 “坑” 及原理分析</title>
      <link>https://note.lican.site/posts/posts/go/go-tips-interface/</link>
      <pubDate>Mon, 05 Apr 2021 16:12:59 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/go-tips-interface/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;前几天在读者交流群里看到一位小伙伴，针对 interface 的使用有了比较大的疑惑。&lt;/p&gt;&#xA;&lt;p&gt;无独有偶，我也在网上看到有小伙伴在 Go 面试的时候被问到了：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/36d7ecb0da9e4b32a493dedce6ebc691~tplv-k3u1fbpfcp-watermark.image&#34; alt=&#34;来自网上博客的截图&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;今天特意分享出来让大家避开这个坑。&lt;/p&gt;&#xA;&lt;h2 id=&#34;例子一&#34;&gt;例子一&lt;/h2&gt;&#xA;&lt;p&gt;第一个例子，如下代码：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;interface&lt;/span&gt;{}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt; = (&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;)(&lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;你觉得输出结果是什么呢？&lt;/p&gt;&#xA;&lt;p&gt;答案是：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;false&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;为什么不是 &lt;code&gt;true&lt;/code&gt;。明明都已经强行置为 &lt;code&gt;nil&lt;/code&gt; 了。是不是 Go 编译器有问题？&lt;/p&gt;&#xA;&lt;h2 id=&#34;例子二&#34;&gt;例子二&lt;/h2&gt;&#xA;&lt;p&gt;第二个例子，如下代码：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;byte&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;interface&lt;/span&gt;{}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;in&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;in&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;in&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;你觉得输出结果是什么呢？&lt;/p&gt;&#xA;&lt;p&gt;答案是：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;lt;nil&amp;gt; true&#xA;&amp;lt;nil&amp;gt; true&#xA;&amp;lt;nil&amp;gt; false&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这可就更奇怪了，为什么刚刚声明出来的 &lt;code&gt;data&lt;/code&gt; 和 &lt;code&gt;in&lt;/code&gt; 变量，确实是输出结果是 &lt;code&gt;nil&lt;/code&gt;，判断结果也是 &lt;code&gt;true&lt;/code&gt;。&lt;/p&gt;&#xA;&lt;p&gt;怎么把变量 &lt;code&gt;data&lt;/code&gt; 一赋予给变量 &lt;code&gt;in&lt;/code&gt;，世界就变了？输出结果依然是 &lt;code&gt;nil&lt;/code&gt;，但判定却变成了 &lt;code&gt;false&lt;/code&gt;。&lt;/p&gt;&#xA;&lt;p&gt;和上面的第一个例子结果类似，真是神奇。&lt;/p&gt;&#xA;&lt;h2 id=&#34;原因&#34;&gt;原因&lt;/h2&gt;&#xA;&lt;p&gt;interface 判断与想象中不一样的根本原因是，interface 并不是一个指针类型，虽然他看起来很像，以至于误导了不少人。&lt;/p&gt;&#xA;&lt;p&gt;我们钻下去 interface，interface 共有两类数据结构：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://imgkr2.cn-bj.ufileos.com/560dcab5-e436-4a2c-bfee-6eba6faee1d2.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&amp;amp;Signature=HF1wka8F9MTc%252FVCTrMjhASpeeE4%253D&amp;amp;Expires=1615900476&#34; alt=&#34;&#34;&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 群友提问：学习 defer 时很懵逼，这道不会做！</title>
      <link>https://note.lican.site/posts/posts/go/go-tips-defer/</link>
      <pubDate>Mon, 05 Apr 2021 16:10:51 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/go-tips-defer/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;前几天在读者交流群里看到一位小伙伴，在向大家咨询 Go 相关的技术问题。&#xA;疑问是：“&lt;strong&gt;各位大佬，我在学习 defer 遇到闭包的时候很懵逼，谁比较明白，能指点？&lt;/strong&gt;”&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://image.eddycjy.com/36e2b86b536909f265b84db24dcd80c6.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;疑问&#34;&gt;疑问&lt;/h2&gt;&#xA;&lt;p&gt;他的疑问是下面这道 Go 语言的 defer 题目，大家一起看看：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func main() {&#xA;&#x9;var whatever [6]struct{}&#xA;&#x9;for i := range whatever {&#xA;&#x9;&#x9;defer func() {&#xA;&#x9;&#x9;&#x9;fmt.Println(i)&#xA;&#x9;&#x9;}()&#xA;&#x9;}&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;请自己先想一下输出的结果答案是什么。&lt;/p&gt;&#xA;&lt;p&gt;这位小伙伴按自己的理解后，认为应当输出 xx。但最终的输出结果，可能与其思考的有所偏差，一时想不通。&lt;/p&gt;&#xA;&lt;h3 id=&#34;解惑&#34;&gt;解惑&lt;/h3&gt;&#xA;&lt;p&gt;这段程序的输出结果是：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;5&#xA;5&#xA;5&#xA;5&#xA;5&#xA;5&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;为什么全是 5，为什么不是 0, 1, 2, 3, 4, 5 这样的输出结果呢？&lt;/p&gt;&#xA;&lt;p&gt;其根本原因是&lt;strong&gt;闭包&lt;/strong&gt;所导致的，有两点原因：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;在 &lt;code&gt;for&lt;/code&gt; 循环结束后，局部变量 &lt;code&gt;i&lt;/code&gt; 的值已经是 5 了，并且 &lt;code&gt;defer &lt;/code&gt;的闭包是直接引用变量的 i。&lt;/li&gt;&#xA;&lt;li&gt;结合&lt;code&gt;defer&lt;/code&gt; 关键字的特性，可得知会在 &lt;code&gt;main&lt;/code&gt; 方法主体结束后再执行。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;结合上述，最终输出的结果是已经自增完毕的 5。&lt;/p&gt;&#xA;&lt;h3 id=&#34;进一步思考&#34;&gt;进一步思考&lt;/h3&gt;&#xA;&lt;p&gt;既然了解了为什么，我们再变形一下。再看看另外一种情况，代码如下：&lt;/p&gt;</description>
    </item>
    <item>
      <title>问个 Go 问题，字符串 len 为 0 和 字符串为空 ，有啥区别？</title>
      <link>https://note.lican.site/posts/posts/go/go-tips-lenstr/</link>
      <pubDate>Mon, 05 Apr 2021 16:09:14 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/go-tips-lenstr/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;前几天在微信群看到几位大佬在讨论一个问题： ”&lt;strong&gt;字符串 len == 0 和 字符串 == &amp;quot;&amp;quot; ，有啥区别&lt;/strong&gt;？“&lt;/p&gt;&#xA;&lt;p&gt;这是一个比较小的细节点，同时也勾起了我的好奇心，因此今天这篇文章就和大家一起研究一下他们两者有没有区别，谁的性能更好一些？&lt;/p&gt;&#xA;&lt;h2 id=&#34;测试方法&#34;&gt;测试方法&lt;/h2&gt;&#xA;&lt;p&gt;在测试的方法中，我们分别声明了 &lt;code&gt;Test1&lt;/code&gt; 和 &lt;code&gt;Test2&lt;/code&gt; 方法：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func Test1() bool {&#xA;&#x9;var v string&#xA;&#x9;if v == &amp;#34;&amp;#34; {&#xA;&#x9;&#x9;return true&#xA;&#x9;}&#xA;&#x9;return false&#xA;}&#xA;&#xA;func Test2() bool {&#xA;&#x9;var v string&#xA;&#x9;if len(v) == 0 {&#xA;&#x9;&#x9;return true&#xA;&#x9;}&#xA;&#x9;return false&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;在方法内部仅做了简单的变量类型声明，分别以 字符串 == &amp;quot;&amp;quot; 和 字符串 len == 0 为判断依据。&lt;/p&gt;&#xA;&lt;h2 id=&#34;测试用例&#34;&gt;测试用例&lt;/h2&gt;&#xA;&lt;p&gt;编写两个方法的 Benchmark，用于后续的性能测试：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func BenchmarkTest1(b *testing.B) {&#xA;&#x9;for i := 0; i &amp;lt; b.N; i++ {&#xA;&#x9;&#x9;Test1()&#xA;&#x9;}&#xA;}&#xA;&#xA;func BenchmarkTest2(b *testing.B) {&#xA;&#x9;for i := 0; i &amp;lt; b.N; i++ {&#xA;&#x9;&#x9;Test2()&#xA;&#x9;}&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;结果分析&#34;&gt;结果分析&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ go test --bench=. -benchmem&#xA;goos: darwin&#xA;goarch: amd64&#xA;BenchmarkTest1-4   &#x9;1000000000&#x9;         0.305 ns/op&#x9;       0 B/op&#x9;       0 allocs/op&#xA;BenchmarkTest2-4   &#x9;1000000000&#x9;         0.305 ns/op&#x9;       0 B/op&#x9;       0 allocs/op&#xA;PASS&#xA;ok  &#x9;_/Users/eddycjy/go-application/awesomeProject/tests&#x9;0.688s&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;从多次测试的结果来看，两者比较：&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 群友提问：Goroutine 数量控制在多少合适，会影响 GC 和调度？</title>
      <link>https://note.lican.site/posts/posts/go/go-tips-goroutinenums/</link>
      <pubDate>Mon, 05 Apr 2021 16:08:18 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/go-tips-goroutinenums/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;前几天在读者交流群里看到一位小伙伴，发出了一个致命提问，那就是：“&lt;strong&gt;单机的 goroutine 数量控制在多少比较合适？&lt;/strong&gt;”。&lt;/p&gt;&#xA;&lt;p&gt;也许你和群内小伙伴第一反应一样，会答复 “控制多少，我觉得没有定论”。&lt;/p&gt;&#xA;&lt;p&gt;紧接着延伸出了更进一步的疑惑：“&lt;strong&gt;goroutine 太多了会影响 gc 和调度吧，主要是怎么预算这个数是合理的呢？&lt;/strong&gt;”&lt;/p&gt;&#xA;&lt;p&gt;这是本文要进行探讨的主体，因此本文的结构会是先探索基础知识，再一步步揭开，深入理解这个问题。&lt;/p&gt;&#xA;&lt;h2 id=&#34;goroutine-是什么&#34;&gt;Goroutine 是什么&lt;/h2&gt;&#xA;&lt;p&gt;Go 语言作为一个新生编程语言，其令人喜爱的特性之一就是 goroutine。Goroutine 是一个由 Go 运行时管理的轻量级线程，一般称其为 “协程”。&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;go f(x, y, z)&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;操作系统本身是无法明确感知到 Goroutine 的存在的，Goroutine 的操作和切换归属于 “用户态” 中。&lt;/p&gt;&#xA;&lt;p&gt;Goroutine 由特定的调度模式来控制，以 “多路复用” 的形式运行在操作系统为 Go 程序分配的几个系统线程上。&lt;/p&gt;&#xA;&lt;p&gt;同时创建 Goroutine 的开销很小，初始只需要 2-4k 的栈空间。Goroutine 本身会根据实际使用情况进行自伸缩，非常轻量。&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func say(s string) {&#xA;&#x9;for i := 0; i &amp;lt; 9999999; i++ {&#xA;&#x9;&#x9;time.Sleep(100 * time.Millisecond)&#xA;&#x9;&#x9;fmt.Println(s)&#xA;&#x9;}&#xA;}&#xA;&#xA;func main() {&#xA;&#x9;go say(&amp;#34;煎鱼&amp;#34;)&#xA;&#x9;say(&amp;#34;你好&amp;#34;)&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;人称可以开几百几千万个的协程小霸王，是 Go 语言的得意之作之一。&lt;/p&gt;&#xA;&lt;h2 id=&#34;调度是什么&#34;&gt;调度是什么&lt;/h2&gt;&#xA;&lt;p&gt;既然有了用户态的代表 Goroutine，操作系统又看不到他。必然需要有某个东西去管理他，才能更好的运作起来。&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
