<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Tony Bai &#187; 并发编程</title>
	<atom:link href="http://tonybai.com/tag/%e5%b9%b6%e5%8f%91%e7%bc%96%e7%a8%8b/feed/" rel="self" type="application/rss+xml" />
	<link>https://tonybai.com</link>
	<description>一个程序员的心路历程</description>
	<lastBuildDate>Wed, 15 Apr 2026 23:35:12 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>别再无脑 go func() 了！Go 资深布道师 Dave Cheney 的 Goroutine 管理哲学</title>
		<link>https://tonybai.com/2026/04/13/dave-cheney-goroutine-management-philosophy/</link>
		<comments>https://tonybai.com/2026/04/13/dave-cheney-goroutine-management-philosophy/#comments</comments>
		<pubDate>Sun, 12 Apr 2026 22:29:12 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[ConcurrencyModel]]></category>
		<category><![CDATA[ConcurrentProgramming]]></category>
		<category><![CDATA[ContextManagement]]></category>
		<category><![CDATA[CooperativeCancellation]]></category>
		<category><![CDATA[DaveCheney]]></category>
		<category><![CDATA[deadlock]]></category>
		<category><![CDATA[errgroup]]></category>
		<category><![CDATA[GCRoots]]></category>
		<category><![CDATA[GC根节点]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[goroutine]]></category>
		<category><![CDATA[GracefulShutdown]]></category>
		<category><![CDATA[HighAvailabilityArchitecture]]></category>
		<category><![CDATA[MemoryLeak]]></category>
		<category><![CDATA[pkg/group]]></category>
		<category><![CDATA[RaceCondition]]></category>
		<category><![CDATA[ServiceGovernance]]></category>
		<category><![CDATA[StructuredConcurrency]]></category>
		<category><![CDATA[SyncWaitGroup]]></category>
		<category><![CDATA[Tomb]]></category>
		<category><![CDATA[上下文管理]]></category>
		<category><![CDATA[优雅关闭]]></category>
		<category><![CDATA[内存泄漏]]></category>
		<category><![CDATA[协同退出]]></category>
		<category><![CDATA[并发模型]]></category>
		<category><![CDATA[并发编程]]></category>
		<category><![CDATA[服务治理]]></category>
		<category><![CDATA[死锁]]></category>
		<category><![CDATA[竞态条件]]></category>
		<category><![CDATA[结构化并发]]></category>
		<category><![CDATA[高可用架构]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=6178</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2026/04/13/dave-cheney-goroutine-management-philosophy 大家好，我是Tony Bai。 在 Go 语言的江湖里，go func() 就像一把绝世好剑。它轻灵、锋利，只需几个字符，就能让你瞬间拥有“分身术”，并发地处理海量任务。Go 团队曾自豪地告诉我们：Goroutine 很廉价，你可以随手启动成千上万个。 于是，我们习惯了在代码里肆意挥洒： HTTP 请求来了？go handle()。 要写日志？go log()。 要发通知？go notify()。 &#8230; &#8230; 我们以为自己掌握了并发的捷径。 但就在去年的 GopherCon Singapore 技术大会上，Go 社区的资深布道师 Dave Cheney，却用一场充满哲学思考的演说，给所有 Gopher 敲响了警钟。 他的核心论点很明确：Goroutine 绝非免费的午餐，它是一种需要付出代价的“有限资源”。如果你只管启动（Start）而不懂如何停止（Stop），你并没有在写高效的并发程序，你只是在为系统埋下慢性自杀的伏笔。 今天，我们就来深度拆解 Dave Cheney 的这场重要演讲，梳理出他在 AI 大模型和微服务时代，为我们总结的 “Goroutine 声明周期管理四大哲学”以及他最终给出的Goroutine管理方案。 哲学一：内存是有价的，而 Goroutine 是“内存之根” Dave Cheney 在演讲开头提出了一个极其硬核的观点：内存不是无限的，它是和数据库连接、文件句柄一样的有限资源。 在 Java 或 C++ 中，我们要时刻担心内存泄漏。但在 Go 里，我们觉得有 [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/2026/dave-cheney-goroutine-management-philosophy-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2026/04/13/dave-cheney-goroutine-management-philosophy">本文永久链接</a> &#8211; https://tonybai.com/2026/04/13/dave-cheney-goroutine-management-philosophy</p>
<p>大家好，我是Tony Bai。</p>
<p>在 Go 语言的江湖里，go func() 就像一把绝世好剑。它轻灵、锋利，只需几个字符，就能让你瞬间拥有“分身术”，并发地处理海量任务。Go 团队曾自豪地告诉我们：Goroutine 很廉价，你可以随手启动成千上万个。</p>
<p>于是，我们习惯了在代码里肆意挥洒：</p>
<ul>
<li>HTTP 请求来了？go handle()。</li>
<li>要写日志？go log()。</li>
<li>要发通知？go notify()。</li>
<li>&#8230; &#8230;</li>
</ul>
<p>我们以为自己掌握了并发的捷径。</p>
<p>但就在去年的 GopherCon Singapore 技术大会上，Go 社区的资深布道师 Dave Cheney，却用<a href="https://www.youtube.com/watch?v=eJLVT157BSs">一场充满哲学思考的演说</a>，给所有 Gopher 敲响了警钟。</p>
<p>他的核心论点很明确：Goroutine 绝非免费的午餐，它是一种需要付出代价的“有限资源”。如果你只管启动（Start）而不懂如何停止（Stop），你并没有在写高效的并发程序，你只是在为系统埋下慢性自杀的伏笔。</p>
<p>今天，我们就来深度拆解 Dave Cheney 的这场重要演讲，梳理出他在 AI 大模型和微服务时代，为我们总结的 “Goroutine 声明周期管理四大哲学”以及他最终给出的Goroutine管理方案。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/agentic-api-in-action-qr.png" alt="" /></p>
<h2>哲学一：内存是有价的，而 Goroutine 是“内存之根”</h2>
<p>Dave Cheney 在演讲开头提出了一个极其硬核的观点：<strong>内存不是无限的，它是和数据库连接、文件句柄一样的有限资源。</strong></p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/dave-cheney-goroutine-management-philosophy-2.png" alt="" /></p>
<p>在 Java 或 C++ 中，我们要时刻担心内存泄漏。但在 Go 里，我们觉得有 <a href="https://tonybai.com/2026/04/07/garbage-collectors-deep-dive">GC（垃圾回收器）</a>在，一切无忧。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/dave-cheney-goroutine-management-philosophy-3.png" alt="" /></p>
<p>然而，Dave 指出了一个被 99% 的人忽略的真相：<strong>在 Go 的世界里，每一个正在运行的 Goroutine，都是一个“GC 根节点（GC Root）”。</strong></p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/dave-cheney-goroutine-management-philosophy-4.png" alt="" /></p>
<p><strong>什么意思？</strong></p>
<p>只要一个 Goroutine 还在运行，它所引用的所有内存、它栈上的所有变量、它指向的所有堆对象，GC 都<strong>绝对不敢</strong>回收。</p>
<blockquote>
<p>“你可以关闭一个文件，可以解锁一个互斥锁。但你如何‘回收’一个失控的 Goroutine？”</p>
</blockquote>
<p>如果你启动了一个 Goroutine 后失去了对它的追踪，它就变成了一个永远无法回收的“内存僵尸”。它不仅自己霸占着 2KB 以上的栈空间，更可能死死拽着几个 GB 的业务对象不撒手。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/dave-cheney-goroutine-management-philosophy-5.png" alt="" /></p>
<h2>哲学二：永远不要启动一个你不知道如何停止的 Goroutine</h2>
<p>这是 Dave Cheney 演讲中最核心的一句军规：<strong>Never start a goroutine without knowing how it will stop.</strong></p>
<p>为了证明“野 Goroutine”的破坏力，Dave 在现场演示了一个极其经典的血泪 Demo。</p>
<p>他写了一个 HTTP 服务器，为了让请求秒回，他把日志记录放到了后台：go logRequest(r)。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/dave-cheney-goroutine-management-philosophy-7.png" alt="" /></p>
<p>接着，他通过重定向标准输出模拟了下游日志系统网络拥堵、写入被阻塞的场景。</p>
<p><strong>恐怖的一幕发生了：</strong></p>
<p>服务器内存开始疯狂飙升，每秒钟都有成百上千个新的 Goroutine 被创建，但因为输出被阻塞，它们全都卡在写入的那一行，一个都死不掉。<br />
不到一分钟，整个程序因为 OOM（内存溢出）当场暴毙。</p>
<p><strong>Dave 的结论非常冷酷：</strong></p>
<p>启动一个 Goroutine 只需要 1 微秒，但如果不考虑它的“死法”，这个 Goroutine 最终会成为杀掉你整个集群的凶手。</p>
<h2>哲学三：不要强迫它停，要“优雅地求它停”</h2>
<p><img src="https://tonybai.com/wp-content/uploads/2026/dave-cheney-goroutine-management-philosophy-8.png" alt="" /></p>
<p>在 Java 中，曾经有一个 thread.stop() 方法，后来被禁用了，因为它会引发不可控的资源损坏。Go 语言聪明地避开了这个坑：<strong>Go 没有任何一种方式，能让一个 Goroutine 强行停止另一个。</strong></p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/dave-cheney-goroutine-management-philosophy-6.png" alt="" /></p>
<p>你只能通过 <strong>“协同（Cooperation）”</strong>。</p>
<p>Dave 强调，defer 是 Goroutine 的“临终遗言”。所有的资源释放（文件关闭、锁解除）都必须放在 defer 里。</p>
<p>而管理这一切的唯一“生死符”，就是 <strong>Context</strong>。</p>
<p>在 Dave 的哲学里，一个合格的后台服务函数，必须长成这样：</p>
<pre><code class="go">func (s *Service) Run(ctx context.Context) error {
    // 1. 临终遗言：无论如何，最后一定要清理战场
    defer s.cleanup() 

    for {
        select {
        case &lt;-ctx.Done():
            // 2. 收到“生死符”，优雅退出
            return ctx.Err()
        case task := &lt;-s.taskChan:
            s.process(task)
        }
    }
}
</code></pre>
<p>你必须给 Goroutine 一个“想得开”的机会，让它在收到 ctx.Done() 时，带着所有的 defer 体面地离开。</p>
<h2>哲学四：把并发权留给调用者，而不是库</h2>
<p><img src="https://tonybai.com/wp-content/uploads/2026/dave-cheney-goroutine-management-philosophy-9.png" alt="" /></p>
<p>这是 Dave Cheney 给库开发者（Library Authors）提出的最高阶要求。</p>
<p>他引用了另一位大神 Peter Bourgon 的话：<strong>“Leave concurrency to the caller.”</strong></p>
<p><strong>一个设计糟糕的库：</strong> 在你调用 NewProvider() 的时候，悄悄在后台启动了一个 Goroutine 去跑心跳，却没给你返回任何停止它的句柄。这种库是不可靠的。</p>
<p><strong>一个具有“管理哲学”的库：</strong> 即使它需要后台运行，它也应该把那个 Run 函数暴露给用户，让用户自己决定：</p>
<ul>
<li>是开一个 Goroutine 去跑它？</li>
<li>还是把它扔进一个 errgroup 里集中管控？</li>
<li>还是干脆同步运行它？</li>
</ul>
<p>只有这样，作为顶层架构师的你，才能真正实现所有子系统的 <strong>“同生共死”</strong>。</p>
<h2>历史的挣扎：从 Tomb 到 Errgroup，我们与“失控”的斗争</h2>
<p>事实上，Go 社区与“Goroutine 管理”这个恶魔的斗争，从 2012 年就开始了。Dave带着我们一起回顾了一下社区的方案，虽然每个方案都不完美！</p>
<p><strong>第一代武器：Tomb (坟墓)</strong></p>
<p>来自 Canonical（Ubuntu 母公司）的 Juju 项目，发明了 tomb 包。它通过一个 t.Go() 方法来启动 Goroutine，并用一个 t.Wait() 来等待它们全部结束。但它的缺点是，如何通知这些 Goroutine“你们该停了”，依然需要开发者手动传来传去。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/dave-cheney-goroutine-management-philosophy-10.png" alt="" /></p>
<p><strong>第二代武器：Errgroup</strong></p>
<p>由 Go 社区大神 Brad Fitzpatrick 编写的 errgroup，极大地简化了“并发执行一组任务，并收集第一个错误”的场景。但它同样没有解决“如何优雅地通知所有任务提前中止”的问题。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/dave-cheney-goroutine-management-philosophy-11.png" alt="" /></p>
<p><strong>第三代武器：OK Log 的 group 包</strong></p>
<p>由 Peter Bourgon 设计的 group 包，首次引入了一个极其优雅的范式。它要求你在添加一个任务时，必须同时提供两个函数：一个 execute 函数（如何启动），和一个 interrupt 函数（如何打断）。</p>
<p>这是一种“契约式”的设计，强制开发者在启动一个 Goroutine 的时候，就必须想好如何杀死它。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/dave-cheney-goroutine-management-philosophy-12.png" alt="" /></p>
<h2>Dave Cheney 的Goroutine管理方案</h2>
<p>在吸收了上述哲学以及社区尝试后，Dave 给出了一个现代 Go 微服务的“标准起手式”，当然也是他自己的Goroutine管理方案：pkg/group。</p>
<p>在吸收了社区十几年来的所有经验和教训之后，Dave Cheney 在演讲的最后，亮出了他自己多年来在无数个项目中沉淀下来的“终极武器”——一个同样名为 group 的、集大成的 Goroutine 管理库：pkg/group，也可以认为是一个现代 Go 微服务的“标准起手式”：</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/dave-cheney-goroutine-management-philosophy-15.png" alt="" /></p>
<p>在 Dave Cheney 的 group 里，你添加的每一个任务，都必须是一个接受 context.Context 作为参数的函数。</p>
<pre><code class="go">g.Add(func(ctx context.Context) error {
    // ...
})
</code></pre>
<p>Context 成了所有 Goroutine 唯一的“生死符”。无论是超时、是上游请求被取消、还是整个服务收到了 SIGTERM 信号准备关闭，都会通过 ctx.Done() 这个唯一的通道，通知到每一个角落。</p>
<p>在 Dave Cheney 的 group 中，任何一个子 Goroutine 发生的 panic，都不会导致整个进程崩溃。它会被 recover 住，转化为一个 error，然后触发整个 group 的优雅关闭流程。</p>
<p>pkg/group的使用典型示例如下：</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/dave-cheney-goroutine-management-philosophy-13.png" alt="" /><br />
<img src="https://tonybai.com/wp-content/uploads/2026/dave-cheney-goroutine-management-philosophy-14.png" alt="" /></p>
<p>在这段代码里，所有的后台服务被捆绑成了一个“命运共同体”。任何一个服务失败，或者 k8s 发来关闭 Pod 的信号，都会导致所有服务一起进入优雅关闭流程，确保数据不丢失、连接被妥善断开。</p>
<h2>小结</h2>
<p>从“启动”到“坟墓”，Dave Cheney 为我们揭示了并发编程的下半场：<strong>Goroutine管理</strong></p>
<p>go func() 赋予了我们随手创造并发的权力，但真正体现架构师功力的，是你管理这些并发生命周期的责任感。</p>
<p>下一次，当你在键盘上敲下那几个字符时，请停顿一秒。</p>
<p>想一想：<strong>这把剑挥出去，你还能收回来吗？</strong></p>
<p>资料链接：https://www.youtube.com/watch?v=eJLVT157BSs</p>
<hr />
<p><strong>今日互动探讨：</strong></p>
<p>在你的项目中，是否曾遇到过 Goroutine 泄漏导致的内存灾难？你是如何定位出那个“失踪”的 Goroutine 的？</p>
<p>欢迎在评论区分享你的避坑经验!</p>
<hr />
<p>还在为“复制粘贴喂AI”而烦恼？我的新专栏 <strong>《<a href="http://gk.link/a/12EPd">AI原生开发工作流实战</a>》</strong> 将带你：</p>
<ul>
<li>告别低效，重塑开发范式</li>
<li>驾驭AI Agent(Claude Code)，实现工作流自动化</li>
<li>从“AI使用者”进化为规范驱动开发的“工作流指挥家”</li>
</ul>
<p>扫描下方二维码，开启你的AI原生开发之旅。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/ai-native-dev-workflow-qr.png" alt="" /></p>
<hr />
<p>你的Go技能，是否也卡在了“熟练”到“精通”的瓶颈期？</p>
<ul>
<li>想写出更地道、更健壮的Go代码，却总在细节上踩坑？</li>
<li>渴望提升软件设计能力，驾驭复杂Go项目却缺乏章法？</li>
<li>想打造生产级的Go服务，却在工程化实践中屡屡受挫？</li>
</ul>
<p>继《<a href="http://gk.link/a/10AVZ">Go语言第一课</a>》后，我的《<a href="http://gk.link/a/12yGY">Go语言进阶课</a>》终于在极客时间与大家见面了！</p>
<p>我的全新极客时间专栏 《<a href="http://gk.link/a/12yGY">Tony Bai·Go语言进阶课</a>》就是为这样的你量身打造！30+讲硬核内容，带你夯实语法认知，提升设计思维，锻造工程实践能力，更有实战项目串讲。</p>
<p>目标只有一个：助你完成从“Go熟练工”到“Go专家”的蜕变！ 现在就加入，让你的Go技能再上一个新台阶！</p>
<p><img src="https://tonybai.com/wp-content/uploads/course-card/iamtonybai-banner-2.gif" alt="" /></p>
<hr />
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求，请扫描下方公众号二维码，与我私信联系。</p>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p style='text-align:left'>&copy; 2026, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2026/04/13/dave-cheney-goroutine-management-philosophy/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>你每天敲下的 go func()，藏着这位 92 岁老人的毕生心血</title>
		<link>https://tonybai.com/2026/03/11/in-memory-of-tony-hoare/</link>
		<comments>https://tonybai.com/2026/03/11/in-memory-of-tony-hoare/#comments</comments>
		<pubDate>Wed, 11 Mar 2026 09:38:28 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[ArchitectureDesign]]></category>
		<category><![CDATA[Channel]]></category>
		<category><![CDATA[cloudnative]]></category>
		<category><![CDATA[ConcurrentProgramming]]></category>
		<category><![CDATA[CSP]]></category>
		<category><![CDATA[DataFlow]]></category>
		<category><![CDATA[deadlock]]></category>
		<category><![CDATA[EngineeringPhilosophy]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[goroutine]]></category>
		<category><![CDATA[HighConcurrency]]></category>
		<category><![CDATA[Mutex]]></category>
		<category><![CDATA[PerformanceOptimization]]></category>
		<category><![CDATA[quicksort]]></category>
		<category><![CDATA[RaceCondition]]></category>
		<category><![CDATA[SequentialProcesses]]></category>
		<category><![CDATA[SharedMemory]]></category>
		<category><![CDATA[SystemProgramming]]></category>
		<category><![CDATA[TonyHoare]]></category>
		<category><![CDATA[云原生]]></category>
		<category><![CDATA[互斥锁]]></category>
		<category><![CDATA[共享内存]]></category>
		<category><![CDATA[协程]]></category>
		<category><![CDATA[工程哲学]]></category>
		<category><![CDATA[并发编程]]></category>
		<category><![CDATA[快速排序]]></category>
		<category><![CDATA[性能优化]]></category>
		<category><![CDATA[托尼霍尔]]></category>
		<category><![CDATA[数据流动]]></category>
		<category><![CDATA[架构设计]]></category>
		<category><![CDATA[死锁]]></category>
		<category><![CDATA[竞态条件]]></category>
		<category><![CDATA[系统级编程]]></category>
		<category><![CDATA[通信顺序进程]]></category>
		<category><![CDATA[通道]]></category>
		<category><![CDATA[顺序进程]]></category>
		<category><![CDATA[高并发]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=6021</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2026/03/11/in-memory-of-tony-hoare 大家好，我是Tony Bai。 在这个由代码构建的现代世界里，有些名字如同星辰般指引着航向。但遗憾的是，2026 年 3 月 5 日，其中一颗最明亮的星辰熄灭了。 图灵奖得主、快速排序（Quicksort）发明者、CSP（通信顺序进程）理论之父 Tony Hoare（托尼·霍尔）与世长辞，享年 92 岁。 也许你并不熟悉这个名字。但只要你是一个程序员，你就一定在面试时手写过他发明的快速排序；如果你是一个 Go 开发者，那你每天在键盘上敲下的每一个 go func() 和 make(chan int)，都在调用着他留给这个世界的伟大的遗产。 今天，让我们暂时放下手头的 CRUD，跨越半个世纪的时间洪流，去看看这位非典型天才，是如何用他那近乎神迹的洞察力，赐予了 Go 语言制霸云原生时代的“并发灵魂”。 被“共享内存”支配的黑暗时代 在讲 Tony Hoare 有多伟大之前，我们必须先回忆一下，在他提出那套神级理论之前，程序员们在并发编程的泥潭里经历了怎样暗无天日的挣扎。 随着多核时代的到来，程序需要同时执行多个任务。传统的思路极其简单粗暴：共享内存（Shared Memory）。 一堆线程就像一群饿狼，死死盯着同一块内存区域。为了防止数据被写乱，程序员们被迫发明了互斥锁（Mutex）、信号量（Semaphore）。你必须极其小心地、以上帝视角去加锁、读写、释放锁。 只要你稍有不慎，忘记解锁，或者加锁顺序反了，死锁（Deadlock）和竞态条件（Race Condition） 就会像幽灵一样找上门来。程序在本地跑得好好的，一上生产环境就离奇崩溃，且极难复现、极难调试。 那是一个属于并发编程的“黑暗时代”。天下程序员苦“共享内存与锁”久矣，却找不到破局之法。 从古典哲学到“六便士的赌注” 就在整个计算机科学界在锁的泥潭里打滚时，Tony Hoare 站了出来。 有趣的是，Tony 并非科班出身。他在大学修读的竟然是古典学与哲学，后来又在皇家海军服役期间接受了高强度的俄语训练。这种看似“不务正业”的跨学科背景，赋予了他极其严密的逻辑思辨能力和哲学视角的解构能力。 他年轻时有个极其经典的轶事：在一家公司打工时，老板让他实现 Shellsort（希尔排序）。Tony 完成任务后，怯生生地对老板说：“我知道一种比这快得多的算法。” 老板不屑一顾：“我跟你赌六便士（大约几毛钱），你肯定不知道！” 于是，Tony 写出了那个后来被印在全世界每一本数据结构教材里的算法——快速排序（Quicksort）。他不仅赢走了那六便士，还顺手改变了世界。 而在面对并发编程的“绝症”时，Tony 再次展现了他哲学般的降维打击能力。 惊世骇俗的 [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/2026/in-memory-of-tony-hoare-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2026/03/11/in-memory-of-tony-hoare">本文永久链接</a> &#8211; https://tonybai.com/2026/03/11/in-memory-of-tony-hoare</p>
<p>大家好，我是Tony Bai。</p>
<p>在这个由代码构建的现代世界里，有些名字如同星辰般指引着航向。但遗憾的是，2026 年 3 月 5 日，其中一颗最明亮的星辰熄灭了。</p>
<p>图灵奖得主、快速排序（Quicksort）发明者、CSP（通信顺序进程）理论之父 <a href="https://blog.computationalcomplexity.org/2026/03/tony-hoare-1934-2026.html">Tony Hoare（托尼·霍尔）与世长辞，享年 92 岁</a>。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/in-memory-of-tony-hoare-2.jpg" alt="" /></p>
<p>也许你并不熟悉这个名字。但只要你是一个程序员，你就一定在面试时手写过他发明的快速排序；<strong>如果你是一个 Go 开发者，那你每天在键盘上敲下的每一个 go func() 和 make(chan int)，都在调用着他留给这个世界的伟大的遗产。</strong></p>
<p>今天，让我们暂时放下手头的 CRUD，跨越半个世纪的时间洪流，去看看这位非典型天才，是如何用他那近乎神迹的洞察力，赐予了 Go 语言制霸云原生时代的“并发灵魂”。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/paid/go-concurrency-mental-model-qr.png" alt="" /></p>
<h2>被“共享内存”支配的黑暗时代</h2>
<p>在讲 Tony Hoare 有多伟大之前，我们必须先回忆一下，在他提出那套神级理论之前，程序员们在并发编程的泥潭里经历了怎样暗无天日的挣扎。</p>
<p>随着多核时代的到来，程序需要同时执行多个任务。传统的思路极其简单粗暴：共享内存（Shared Memory）。</p>
<p>一堆线程就像一群饿狼，死死盯着同一块内存区域。为了防止数据被写乱，程序员们被迫发明了互斥锁（Mutex）、信号量（Semaphore）。你必须极其小心地、以上帝视角去加锁、读写、释放锁。</p>
<p>只要你稍有不慎，忘记解锁，或者加锁顺序反了，<strong>死锁（Deadlock）和竞态条件（Race Condition）</strong> 就会像幽灵一样找上门来。程序在本地跑得好好的，一上生产环境就离奇崩溃，且极难复现、极难调试。</p>
<p>那是一个属于并发编程的“黑暗时代”。天下程序员苦“共享内存与锁”久矣，却找不到破局之法。</p>
<h2>从古典哲学到“六便士的赌注”</h2>
<p>就在整个计算机科学界在锁的泥潭里打滚时，Tony Hoare 站了出来。</p>
<p>有趣的是，Tony 并非科班出身。他在大学修读的竟然是<strong>古典学与哲学</strong>，后来又在皇家海军服役期间接受了高强度的<strong>俄语</strong>训练。这种看似“不务正业”的跨学科背景，赋予了他极其严密的逻辑思辨能力和哲学视角的解构能力。</p>
<p>他年轻时有个极其经典的轶事：在一家公司打工时，老板让他实现 Shellsort（希尔排序）。Tony 完成任务后，怯生生地对老板说：“我知道一种比这快得多的算法。” 老板不屑一顾：“我跟你赌六便士（大约几毛钱），你肯定不知道！”</p>
<p>于是，Tony 写出了那个后来被印在全世界每一本数据结构教材里的算法——<strong>快速排序（Quicksort）</strong>。他不仅赢走了那六便士，还顺手改变了世界。</p>
<p>而在面对并发编程的“绝症”时，Tony 再次展现了他哲学般的降维打击能力。</p>
<h2>惊世骇俗的 CSP 理论</h2>
<p>1978 年，Tony Hoare 发表了一篇名为《通信顺序进程》（<a href="https://en.wikipedia.org/wiki/Communicating_sequential_processes">Communicating Sequential Processes</a>, 简称 <strong>CSP</strong>）的学术论文。</p>
<p>宛如一道闪电，这篇论文劈开了并发编程的混沌。</p>
<p>Tony 的哲学思维告诉他：既然共享内存那么容易出错，那我们<strong>干脆就不要共享内存了！</strong></p>
<p>在 CSP 理论中，系统被划分为多个独立的、顺序执行的黑盒（进程）。它们之间没有任何共享状态。当它们需要协作时，唯一的交互方式是通过一条极其明确的管道（Channel）来<strong>“发送和接收消息”</strong>。</p>
<p>这就像是现实生活中的流水线工人：每个人只管自己手头的活（顺序执行），做完了就通过传送带（Channel）递给下一个人。没人去抢同一个零件，自然就不需要打架（加锁）。</p>
<p>这种高度抽象的数学模型，完美地将复杂的并发控制，降维成了简单的数据流动。</p>
<h2>Go 语言与云原生的基石</h2>
<p>理论是伟大的，但在 1978 年，CSP 受限于当时的硬件架构，很难大规模工程化普及。它在学术界的象牙塔里，静静等待着一个能将它发扬光大的使者。</p>
<p>30 年后，谷歌的一间办公室里，Rob Pike、Ken Thompson 等几位大神正被 C++ 的并发折磨得痛不欲生。他们决定<a href="https://tonybai.com/2025/07/03/meet-the-go-team-2012/">创造一门新的语言</a>。</p>
<p>由于 Rob Pike 早年深受 CSP 理论启发，他将 Tony Hoare 的毕生心血，直接刻进了这门新语言的基因里。<strong>这门语言，就是 Go。</strong></p>
<p>Tony Hoare 论文里的晦涩数学模型，在 Go 语言里被具象化为了两个极其优雅的关键字：</p>
<ol>
<li><strong>顺序进程</strong>，演化成了轻量级的 <strong>Goroutine</strong> (go func())。</li>
<li><strong>通信管道</strong>，演化成了强类型的 <strong>Channel</strong> (make(chan int))。</li>
</ol>
<p>Rob Pike 更是将 CSP 的核心思想，提炼成了那句在 Go 圈子里无人不知的至理名言：</p>
<blockquote>
<p><strong>“Do not communicate by sharing memory; instead, share memory by communicating.”</strong><br />
  （不要通过共享内存来通信，而应该通过通信来共享内存。）</p>
</blockquote>
<p>让我们看一眼这被 CSP 灵魂洗礼过的代码，没有任何 sync.Mutex，没有复杂的死锁恐惧，数据的控制权随着流水的管道优雅地传递：</p>
<pre><code class="go">func main() {
    ch := make(chan int) // 创造一条 Tony Hoare 定义的通信管道

    go func() {          // 启动一个 Tony Hoare 定义的顺序进程
        ch &lt;- 42         // 通过通信转移数据
    }()

    fmt.Println(&lt;-ch)    // 完美接收，无需任何锁
}
</code></pre>
<p>Tony Hoare 也许没有预料到，他在半个世纪前写下的论文，会在今天成为支撑全球互联网的基石之一。</p>
<p>当我们谈论云原生时代的 Docker、Kubernetes、Prometheus 时，我们谈论的其实是 Go 语言；而当我们惊叹于 Go 语言能轻松扛起千万级的高并发调度时，我们真正应该感谢的，是底层那个名叫 CSP 的幽灵。</p>
<p>我们每一次扩容容器，底层的字节流都在以 Tony Hoare 所描绘的方式，有条不紊地穿梭于硅片与光纤之间。</p>
<h2>致敬宗师：最好的纪念，是传承他的思想</h2>
<p><a href="https://blog.computationalcomplexity.org/2026/03/tony-hoare-1934-2026.html">Jim Miles 在追忆 Tony 的文章</a>中提到，这位伟大的图灵奖得主极其谦逊。他曾笑着对别人说：<strong>“真正的天才不是一蹴而就的，而是在无数个日夜的深度思考中，为了一个单一问题苦苦挣扎的凡人。”</strong></p>
<p>作为普通的开发者，我们无缘与这位伟人共饮下午茶，或听他亲口讲述那六便士的赌注。但作为工程师，我们对宗师最好的纪念，就是<strong>停止写那些糟糕的、充满死锁风险的并发代码，去真正理解并传承他的设计哲学。</strong></p>
<p>今天，当你再次在 IDE 中敲下那个简短却充满魔力的 go func() 时，请在心底默默向这位智者致敬。</p>
<p>再见了，一代巨匠 Tony Hoare。</p>
<p>您的代码和算法已是不朽。您赐予计算世界的并发灵魂，将伴随着一代又一代的程序员，在无尽的服务器网络中，永不停止地运行下去。</p>
<h2>参考资料</h2>
<ul>
<li>https://en.wikipedia.org/wiki/Communicating_sequential_processes</li>
<li>https://blog.computationalcomplexity.org/2026/03/tony-hoare-1934-2026.html</li>
</ul>
<hr />
<p><strong>今日互动：</strong></p>
<p>你在平时的 Go 开发中，是更喜欢用 Channel（CSP 模型）还是更习惯用 Mutex 锁（共享内存模型）？在并发编程中踩过哪些大坑？</p>
<p>欢迎在评论区分享你的心得！</p>
<hr />
<p><strong>认知跃迁：真正驾驭 Go 的并发灵魂</strong></p>
<p>Tony Hoare 将复杂的并发问题，抽象成了极其优雅的 CSP 理论。但很多 Go 开发者，由于没有看透这层底层哲学，依然在用写 Java/C++（共享内存）的思维来写 Go，最终把 Channel 滥用得一塌糊涂，甚至引发严重的 Goroutine 泄漏。</p>
<p><strong>想要真正吃透 Go 语言的并发灵魂，靠死背语法是绝对不够的。</strong> 你必须深入理解底层调度器（G-M-P 模型）是如何运作的，必须明白何时该用 Channel，何时该退回到 Mutex。</p>
<p>如果你渴望突破并发编程的认知瓶颈，不再只做一个“会调关键字”的熟练工，而是想成为能设计出高可用、极高并发架构的 <strong>Go 资深专家</strong>——</p>
<p>我的极客时间专栏 <a href="http://gk.link/a/12yGY">Go语言进阶课</a> 正是为你量身定制。在这 30+ 讲硬核内容中，我将带你剥开语法糖，直击 Go 并发模型的底层骨架，重塑你的系统级架构审美。</p>
<p>扫描下方二维码，加入专栏。让我们用最扎实的工程实践，去向半个世纪前的伟大思想致敬！</p>
<p><img src="https://tonybai.com/wp-content/uploads/course-card/go-advanced-course-4.png" alt="" /></p>
<hr />
<p>还在为“复制粘贴喂AI”而烦恼？我的新专栏 <strong>《<a href="http://gk.link/a/12EPd">AI原生开发工作流实战</a>》</strong> 将带你：</p>
<ul>
<li>告别低效，重塑开发范式</li>
<li>驾驭AI Agent(Claude Code)，实现工作流自动化</li>
<li>从“AI使用者”进化为规范驱动开发的“工作流指挥家”</li>
</ul>
<p>扫描下方二维码，开启你的AI原生开发之旅。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/ai-native-dev-workflow-qr.png" alt="" /></p>
<hr />
<p><strong>原「Gopher部落」已重装升级为「Go &amp; AI 精进营」知识星球，快来加入星球，开启你的技术跃迁之旅吧！</strong></p>
<p>我们致力于打造一个高品质的 <strong>Go 语言深度学习</strong> 与 <strong>AI 应用探索</strong> 平台。在这里，你将获得：</p>
<ul>
<li><strong>体系化 Go 核心进阶内容:</strong> 深入「Go原理课」、「Go进阶课」、「Go避坑课」等独家深度专栏，夯实你的 Go 内功。</li>
<li><strong>前沿 Go+AI 实战赋能:</strong> 紧跟时代步伐，学习「Go+AI应用实战」、「Agent开发实战课」、「Agentic软件工程课」、「Claude Code开发工作流实战课」、「OpenClaw实战分享」等，掌握 AI 时代新技能。 </li>
<li><strong>星主 Tony Bai 亲自答疑:</strong> 遇到难题？星主第一时间为你深度解析，扫清学习障碍。</li>
<li><strong>高活跃 Gopher 交流圈:</strong> 与众多优秀 Gopher 分享心得、讨论技术，碰撞思想火花。</li>
<li><strong>独家资源与内容首发:</strong> 技术文章、课程更新、精选资源，第一时间触达。</li>
</ul>
<p>衷心希望「Go &amp; AI 精进营」能成为你学习、进步、交流的港湾。让我们在此相聚，享受技术精进的快乐！欢迎你的加入！</p>
<p><img src="http://image.tonybai.com/img/tonybai/gopher-and-ai-tribe-zsxq-small-card.jpg" alt="img{512x368}" /></p>
<hr />
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求，请扫描下方公众号二维码，与我私信联系。</p>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p style='text-align:left'>&copy; 2026, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2026/03/11/in-memory-of-tony-hoare/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Brad Fitzpatrick 也等不及了！sync.Map 的泛型进化与 sync/v2 的诞生之路</title>
		<link>https://tonybai.com/2025/12/01/proposal-sync-v2/</link>
		<comments>https://tonybai.com/2025/12/01/proposal-sync-v2/#comments</comments>
		<pubDate>Mon, 01 Dec 2025 00:42:14 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[any]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[Boxing]]></category>
		<category><![CDATA[BradFitzpatrick]]></category>
		<category><![CDATA[GC]]></category>
		<category><![CDATA[generics]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[IanLanceTaylor]]></category>
		<category><![CDATA[Interface]]></category>
		<category><![CDATA[iter.Seq2]]></category>
		<category><![CDATA[Iterator]]></category>
		<category><![CDATA[Mutex]]></category>
		<category><![CDATA[Once]]></category>
		<category><![CDATA[standardlibrary]]></category>
		<category><![CDATA[sync.Map]]></category>
		<category><![CDATA[sync.Pool]]></category>
		<category><![CDATA[sync/v2]]></category>
		<category><![CDATA[typealias]]></category>
		<category><![CDATA[TypeSafety]]></category>
		<category><![CDATA[waitgroup]]></category>
		<category><![CDATA[并发编程]]></category>
		<category><![CDATA[性能]]></category>
		<category><![CDATA[接口]]></category>
		<category><![CDATA[提案]]></category>
		<category><![CDATA[标准库]]></category>
		<category><![CDATA[核心团队]]></category>
		<category><![CDATA[泛型]]></category>
		<category><![CDATA[生态分裂]]></category>
		<category><![CDATA[类型别名]]></category>
		<category><![CDATA[类型安全]]></category>
		<category><![CDATA[装箱]]></category>
		<category><![CDATA[迁移]]></category>
		<category><![CDATA[迭代器]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=5461</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2025/12/01/proposal-sync-v2 大家好，我是Tony Bai。 当 Go 核心团队前成员、著名 Gopher、net/http包的设计者 Brad Fitzpatrick 在 GitHub 上留下上图中的这句评论并甩出一个自己移植的库时，我们知道，sync/v2 的到来不仅仅是一个提案，更是一种迫切的刚需。 随着 math/rand/v2 在 Go 1.22, json/v2 在 Go 1.25 中的成功落地，Go 标准库的 v2 化进程似乎已经按下了加速键。今年1月份，Go 核心团队成员 Ian Lance Taylor 就提交了sync/v2 的提案 (#71076)。 这可不仅仅是一次简单的版本号升级，它标志着 Go 语言最核心的并发原语包，也终于要拥抱泛型，告别 interface{} 时代了。 在本文中，我们将深入剖析这份提案的核心内容，探讨它将如何重塑 Go 的并发编程体验，以及社区为此展开的激烈辩论。 核心痛点：any 的原罪 目前的 sync 包，特别是 sync.Map 和 sync.Pool，设计于 Go 支持泛型之前。它们被迫使用 any (即 [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/2025/proposal-sync-v2-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2025/12/01/proposal-sync-v2">本文永久链接</a> &#8211; https://tonybai.com/2025/12/01/proposal-sync-v2</p>
<p>大家好，我是Tony Bai。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/proposal-sync-v2-2.png" alt="" /></p>
<p>当 Go 核心团队前成员、著名 Gopher、net/http包的设计者 Brad Fitzpatrick 在 GitHub 上留下上图中的这句评论并甩出一个自己移植的库时，我们知道，sync/v2 的到来不仅仅是一个提案，更是一种迫切的刚需。</p>
<p>随着 math/rand/v2 在 <a href="https://tonybai.com/2024/02/18/some-changes-in-go-1-22">Go 1.22</a>, <a href="https://tonybai.com/2025/05/15/go-json-v2/">json/v2</a> 在 <a href="https://tonybai.com/2025/08/15/some-changes-in-go-1-25">Go 1.25</a> 中的成功落地，Go 标准库的 v2 化进程似乎已经按下了加速键。今年1月份，Go 核心团队成员 Ian Lance Taylor 就提交了<a href="https://github.com/golang/go/issues/71076"><strong>sync/v2</strong> 的提案 (#71076)</a>。</p>
<p>这可不仅仅是一次简单的版本号升级，它标志着 Go 语言最核心的并发原语包，也终于要拥抱泛型，告别 interface{} 时代了。</p>
<p>在本文中，我们将深入剖析这份提案的核心内容，探讨它将如何重塑 Go 的并发编程体验，以及社区为此展开的激烈辩论。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/paid/google-adk-in-action-qr.png" alt="" /></p>
<h2>核心痛点：any 的原罪</h2>
<p>目前的 sync 包，特别是 sync.Map 和 sync.Pool，设计于 Go 支持泛型之前。它们被迫使用 any (即 interface{}) 来处理各种类型的数据。这带来了两个无法忽视的问题：</p>
<ol>
<li><strong>类型安全缺失</strong>：编译器无法阻止你往一个本该只存字符串的 sync.Map 里塞进一个整数，或者从 sync.Pool 里取出一个你以为是 []byte 实际上是 *bytes.Buffer 的东西。所有的错误只能在运行时通过 panic 暴露。</li>
<li><strong>性能损耗</strong>：将非指针类型（如 int、string）存入 any 类型的容器，必须进行装箱（boxing），这不仅增加了 CPU 开销，更重要的是会产生额外的内存分配，加重 GC 负担。对于追求极致性能的并发场景，这是不可接受的。</li>
</ol>
<p>sync/v2 的提案，就是要通过泛型彻底解决这些问题。</p>
<h2>sync/v2 的新面貌：类型安全与 API 进化</h2>
<p>根据提案，sync/v2 将不仅是 sync 的泛型翻版，它还趁机对 API 进行了现代化的打磨。</p>
<h3>Map[K, V]：终于等到了你</h3>
<p>新的 sync.Map 将拥有两个类型参数 K (comparable) 和 V (any)。</p>
<pre><code class="go">// sync/v2
type Map[K comparable, V any] struct { ... }

// 方法签名变得清晰且类型安全
func (m *Map[K, V]) Load(key K) (value V, ok bool)
func (m *Map[K, V]) Store(key K, value V)
</code></pre>
<p>此外，提案还计划顺应时代潮流，移除了老旧的 Range 方法，取而代之的是返回迭代器的 All 方法：</p>
<pre><code class="go">func (m *Map[K, V]) All() iter.Seq2[K, V]
</code></pre>
<h3>Pool[T]：更安全的资源复用</h3>
<p>sync.Pool 的改造稍微复杂一些。目前的 Pool 有一个导出的 New 字段，这很容易被误用。v2 版的提案曾经历过一次修改，最终方案倾向于移除导出的 New 字段，转而通过构造函数来设定：</p>
<pre><code class="go">type Pool[T any] struct { ... }

// 通过构造函数传入创建新对象的逻辑
func NewPool[T any](newf func() T) *Pool[T]

func (p *Pool[T]) Get() T
func (p *Pool[T]) Put(x T)
</code></pre>
<h2>社区的激辩：v2 真的必要吗？</h2>
<p>提案虽然诱人，但也引发了社区关于 Go 语言演进哲学的激烈讨论。</p>
<p><strong>反方：分裂生态的担忧</strong></p>
<p>有声音质疑：sync 包的大部分类型（如 Mutex, WaitGroup, Once）并不需要泛型。如果为了 Map 和 Pool 而引入整个 sync/v2，会不会导致生态分裂？以后我们是不是要在同一个项目里同时维护 v1 和 v2 的锁？</p>
<p>对此，Ian Lance Taylor 及其支持者给出的方案是：sync/v2 将包含 sync 包的所有类型。对于不需要泛型的类型（如 Mutex），通过<strong>类型别名 (Type Alias)</strong> 将其指向 v1 版本，或者保持 API 完全一致。这样，用户可以平滑迁移，最终完全切换到 v2，而无需混用。</p>
<p><strong>正方：性能与体验的刚需</strong></p>
<p>支持者们（包括 Brad Fitzpatrick）则指出，泛型带来的性能提升和开发体验改善是巨大的。特别是对于 Pool[[]byte] 这样的高频场景，避免每次 Put/Get 时的切片头分配，是实打实的性能红利。</p>
<h2>小结：不仅是代码的升级，更是理念的升级</h2>
<p>sync/v2 的提案目前仍在活跃讨论中，尚未尘埃落定。但它释放了一个明确的信号：<strong>Go 团队正在审慎而坚定地推动标准库的现代化</strong>。</p>
<p>对于我们 Gopher 而言，这意味着：</p>
<ol>
<li><strong>拥抱泛型</strong>：这不再是尝鲜，而很可能是未来的标准范式。</li>
<li><strong>关注性能</strong>：标准库的升级将带来免费的性能提升，特别是对于重度依赖 sync.Map 和 sync.Pool 的项目。</li>
<li><strong>准备迁移</strong>：虽然 Go 承诺兼容性，但 v2 包的引入意味着我们需要开始思考如何优雅地过渡。</li>
</ol>
<p>Brad Fitzpatrick 的“等不及”或许代表了许多资深开发者的心声。让我们拭目以待，看 sync/v2 将如何重塑 Go 的并发编程体验。</p>
<hr />
<p><strong>你的选择是？</strong></p>
<p>面对 sync/v2 带来的泛型红利和潜在的迁移成本，你更倾向于第一时间拥抱它，还是持观望态度？在你的项目中，sync.Map 或 sync.Pool 的性能瓶颈是否真的困扰过你？</p>
<p>欢迎在评论区留下你的看法，让我们一起探讨 Go 标准库的未来！ </p>
<p style='text-align:left'>&copy; 2025, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2025/12/01/proposal-sync-v2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Go 的“简单”幻象：易于上手，难于精通</title>
		<link>https://tonybai.com/2025/11/07/go-simple-illusion-easy-to-learn-hard-to-master/</link>
		<comments>https://tonybai.com/2025/11/07/go-simple-illusion-easy-to-learn-hard-to-master/#comments</comments>
		<pubDate>Fri, 07 Nov 2025 06:28:23 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[AI]]></category>
		<category><![CDATA[append]]></category>
		<category><![CDATA[Bug]]></category>
		<category><![CDATA[Channel]]></category>
		<category><![CDATA[Channelmisuse]]></category>
		<category><![CDATA[cloudnativeapplications]]></category>
		<category><![CDATA[Concurrency]]></category>
		<category><![CDATA[Context]]></category>
		<category><![CDATA[coreconcepts]]></category>
		<category><![CDATA[CPU]]></category>
		<category><![CDATA[do-while]]></category>
		<category><![CDATA[ECS]]></category>
		<category><![CDATA[fanin]]></category>
		<category><![CDATA[fanout]]></category>
		<category><![CDATA[for]]></category>
		<category><![CDATA[foreach]]></category>
		<category><![CDATA[GeekTime]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[go1.24]]></category>
		<category><![CDATA[Goexpert]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[GoLanguageAdvancedCourse]]></category>
		<category><![CDATA[GoLanguageFirstCourse]]></category>
		<category><![CDATA[Gophilosophy]]></category>
		<category><![CDATA[goroutine]]></category>
		<category><![CDATA[GoroutineLeak]]></category>
		<category><![CDATA[GoSkilledWorker]]></category>
		<category><![CDATA[httpserver]]></category>
		<category><![CDATA[iferrnil]]></category>
		<category><![CDATA[implicitdependency]]></category>
		<category><![CDATA[Interfaces]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[k8s]]></category>
		<category><![CDATA[keywords]]></category>
		<category><![CDATA[LLMassistedcoding]]></category>
		<category><![CDATA[memorymodel]]></category>
		<category><![CDATA[monkeypatching]]></category>
		<category><![CDATA[net/http]]></category>
		<category><![CDATA[newbook]]></category>
		<category><![CDATA[nilinterface]]></category>
		<category><![CDATA[operationalcomplexity]]></category>
		<category><![CDATA[Orchestrator]]></category>
		<category><![CDATA[panic]]></category>
		<category><![CDATA[pathtomastery]]></category>
		<category><![CDATA[productionsystem]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[race]]></category>
		<category><![CDATA[RaceConditions]]></category>
		<category><![CDATA[racedetector]]></category>
		<category><![CDATA[redditgolangforum]]></category>
		<category><![CDATA[rollyourown]]></category>
		<category><![CDATA[slices]]></category>
		<category><![CDATA[SoftwareEngineering]]></category>
		<category><![CDATA[standardlibrary]]></category>
		<category><![CDATA[standardlibrarysourcecode]]></category>
		<category><![CDATA[sync.Mutex]]></category>
		<category><![CDATA[synchronizationprimitives]]></category>
		<category><![CDATA[TonyBai]]></category>
		<category><![CDATA[unbufferedchannel]]></category>
		<category><![CDATA[underlyingbehavior]]></category>
		<category><![CDATA[while]]></category>
		<category><![CDATA[互斥锁]]></category>
		<category><![CDATA[共享底层数组]]></category>
		<category><![CDATA[内存]]></category>
		<category><![CDATA[减法哲学]]></category>
		<category><![CDATA[可靠]]></category>
		<category><![CDATA[团队]]></category>
		<category><![CDATA[基础设施]]></category>
		<category><![CDATA[外部系统]]></category>
		<category><![CDATA[学习]]></category>
		<category><![CDATA[密码学工具]]></category>
		<category><![CDATA[并发编程]]></category>
		<category><![CDATA[幻象破灭]]></category>
		<category><![CDATA[异常]]></category>
		<category><![CDATA[思考]]></category>
		<category><![CDATA[性能下降]]></category>
		<category><![CDATA[所见即所得]]></category>
		<category><![CDATA[易于上手]]></category>
		<category><![CDATA[易于维护]]></category>
		<category><![CDATA[易于阅读]]></category>
		<category><![CDATA[极简语法]]></category>
		<category><![CDATA[死锁]]></category>
		<category><![CDATA[画图]]></category>
		<category><![CDATA[第三方库]]></category>
		<category><![CDATA[简单幻象]]></category>
		<category><![CDATA[简单性]]></category>
		<category><![CDATA[精通]]></category>
		<category><![CDATA[线程]]></category>
		<category><![CDATA[继承体系]]></category>
		<category><![CDATA[设计]]></category>
		<category><![CDATA[调试]]></category>
		<category><![CDATA[运行时]]></category>
		<category><![CDATA[进程]]></category>
		<category><![CDATA[重新分配]]></category>
		<category><![CDATA[错误处理]]></category>
		<category><![CDATA[隐式魔法]]></category>
		<category><![CDATA[难于精通]]></category>
		<category><![CDATA[韧性]]></category>
		<category><![CDATA[高可用]]></category>
		<category><![CDATA[高效]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=5362</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2025/11/07/go-simple-illusion-easy-to-learn-hard-to-master 大家好，我是Tony Bai。 “Go 语言看起来如此简单，我的这种假设是错的吗？” 近日，一位刚接触 Go 几个月的新手在reddit golang论坛发出了这样一个真诚的提问。他感觉 Go “超级简单”，并好奇自己是否因为初学者的身份，而忽略了语言中那些“疯狂的复杂性”。 这个问题，立刻引发了社区关注。数百条评论从四面八方涌来，汇成了一场关于 Go 语言简单性本质的深度辩论。最终，社区的集体智慧凝聚成一个经典而又充满辩证性的共识：Go 的简单，是刻意为之的设计；而通往精通之路，则隐藏在简约表象之下的深邃之处。 本文将带你深入探索这座“简单”的冰山，从其光彩照人的水上部分，一直潜入其复杂深邃的水下世界。 “蜜月期”——为什么 Go 语言感觉如此简单？ 对于初学者而言，Go 带来的“简单”感受是真实且强烈的。这并非巧合，而是源于 Go 设计者们一系列深思熟虑的“减法”哲学。 极简的语法与关键字 “25 个关键字，宝贝！” 一位评论者这样感叹道。Go 有意地限制了语言的表面积，仅保留了构建大型系统所必需的核心元素。它只有一个循环结构 for，没有 while、do-while 或 foreach 的变体。这种极简主义，让学习者可以快速掌握语言的全貌，而不必记忆大量特殊语法。 “所见即所得”的代码 一位来自 Java/Python 背景的开发者分享道：“Go 给你的玩具可能更少，但至少你可以相信，它们不会在调试时反咬你一口。” Go 缺乏猴子补丁 (monkey patching)、复杂的继承体系和隐式的魔法，这意味着代码的行为更加可预测。“代码读起来就像它实际运行的样子，即便这意味着多写几行。” “电池自带”的强大标准库 “标准库太棒了，” 社区普遍赞同，“你需要花些时间才能理解，在不引入单个依赖的情况下，你能做多少事情。” 从 HTTP 服务器到密码学工具，Go 的标准库提供了构建现代网络服务所需 90% 的功能，让初学者可以立即开始构建有价值的应用，而无需在茫茫的第三方库中选择和配置。 幻象的破灭——“简单”背后的隐藏复杂性 当“蜜月期”结束，开发者开始构建更复杂的真实世界系统时，Go [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/2025/go-simple-illusion-easy-to-learn-hard-to-master-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2025/11/07/go-simple-illusion-easy-to-learn-hard-to-master">本文永久链接</a> &#8211; https://tonybai.com/2025/11/07/go-simple-illusion-easy-to-learn-hard-to-master</p>
<p>大家好，我是Tony Bai。</p>
<p>“Go 语言看起来如此简单，我的这种假设是错的吗？”</p>
<p>近日，一位刚接触 Go 几个月的新手在reddit golang论坛发出了这样<a href="https://www.reddit.com/r/golang/comments/1oj9jb6/golang_seems_so_simple_am_i_wrong_to_assume_that/">一个真诚的提问</a>。他感觉 Go “超级简单”，并好奇自己是否因为初学者的身份，而忽略了语言中那些“疯狂的复杂性”。</p>
<p>这个问题，立刻引发了社区关注。数百条评论从四面八方涌来，汇成了一场关于 Go 语言简单性本质的深度辩论。最终，社区的集体智慧凝聚成一个经典而又充满辩证性的共识：<strong>Go 的简单，是刻意为之的设计；而通往精通之路，则隐藏在简约表象之下的深邃之处。</strong></p>
<p>本文将带你深入探索这座“简单”的冰山，从其光彩照人的水上部分，一直潜入其复杂深邃的水下世界。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/paid/go-micro-column-2025-pr.png" alt="" /></p>
<h2>“蜜月期”——为什么 Go 语言感觉如此简单？</h2>
<p>对于初学者而言，Go 带来的“简单”感受是真实且强烈的。这并非巧合，而是源于 Go 设计者们一系列深思熟虑的“减法”哲学。</p>
<h3>极简的语法与关键字</h3>
<p>“25 个关键字，宝贝！” 一位评论者这样感叹道。Go 有意地限制了语言的表面积，仅保留了构建大型系统所必需的核心元素。它只有一个循环结构 for，没有 while、do-while 或 foreach 的变体。这种极简主义，让学习者可以快速掌握语言的全貌，而不必记忆大量特殊语法。</p>
<h3>“所见即所得”的代码</h3>
<p>一位来自 Java/Python 背景的开发者分享道：“Go 给你的玩具可能更少，但至少你可以相信，它们不会在调试时反咬你一口。” Go 缺乏猴子补丁 (monkey patching)、复杂的继承体系和隐式的魔法，这意味着代码的行为更加可预测。“代码读起来就像它实际运行的样子，即便这意味着多写几行。”</p>
<h3>“电池自带”的强大标准库</h3>
<p>“标准库太棒了，” 社区普遍赞同，“你需要花些时间才能理解，在不引入单个依赖的情况下，你能做多少事情。” 从 HTTP 服务器到密码学工具，Go 的标准库提供了构建现代网络服务所需 90% 的功能，让初学者可以立即开始构建有价值的应用，而无需在茫茫的第三方库中选择和配置。</p>
<h2>幻象的破灭——“简单”背后的隐藏复杂性</h2>
<p>当“蜜月期”结束，开发者开始构建更复杂的真实世界系统时，Go 的另一面便会逐渐显现。这份复杂性，并非来自语言本身，而是源于 Go 为了维持简单性，而将复杂性“转移”到的地方。</p>
<h3>并发：Go 的“光荣与荆棘”</h3>
<p>这是社区中被提及次数最多的“深水区”。Go 通过 goroutine 和 channel，将并发编程的门槛降到了前所未有的低度。然而，这种易用性也隐藏着巨大的风险。</p>
<blockquote>
<p>“理解并发作为一个概念可能会很复杂，但 Go 让实现它变得简单。”</p>
</blockquote>
<p>但“实现简单”不等于“用对简单”。</p>
<ul>
<li><strong>Goroutine 泄露</strong>：新手很容易创建出无人“负责”的 goroutine，导致其在后台永久运行，悄无声息地消耗内存和 CPU。</li>
<li><strong>竞态条件 (Race Conditions)</strong>：尽管 Go 提供了强大的竞态检测器 (-race)，但理解和避免数据竞争，需要对内存模型和同步原语（如 sync.Mutex）有深刻的理解。</li>
<li><strong>Channel 的滥用</strong>：“我数不清有多少次，人们到处使用 goroutine 和 channel，然后好奇为什么他们的项目变得如此之慢。” Channel 是强大的工具，但错误地使用无缓冲 channel、忘记关闭 channel、或用它来解决本该用互斥锁解决的问题，都会导致死锁、性能下降和难以调试的 bug。</li>
</ul>
<p><strong>精通并发，是区分 Go 新手与专家的第一道分水岭。</strong></p>
<h3>运维复杂性</h3>
<p>Go 的设计哲学，在某些方面将应用程序的韧性责任，从语言运行时“推”给了基础设施。这为 Go 程序带来了一种独特的<strong>运维复杂性</strong>。</p>
<p>最典型的例子就是 <strong>panic 的处理</strong>。</p>
<ul>
<li>在某些语言中（如 Java），一个未捕获的异常通常只会导致单个线程死亡，而整个应用程序进程会默认继续运行。</li>
<li>但在 Go 中，一个未被 recover 的 panic 会导致<strong>整个程序（进程）立即崩溃退出</strong>。Go 语言本身不提供自动重启或进程守护的能力，它将这种“灾难恢复”的职责，明确地交给了程序的运行环境。</li>
</ul>
<p>这意味着，构建一个高可用的 Go 服务，你<strong>必须</strong>依赖外部系统。正如一位资深开发者在讨论中指出的那样：</p>
<blockquote>
<p>“像 panic 这样的东西，要求你在一个编排器（如 K8s/ECS 等）下运行你的生产系统。”</p>
</blockquote>
<p>这种设计选择，对于新手来说可能是一个认知上的巨大跳跃。他们必须明白，Go 程序的健壮性，并不仅仅是代码层面的 if err != nil，更是在<strong>基础设施层面</strong>，通过配置进程管理器（如 systemd）或容器编排器（如 Kubernetes）的健康检查和自动重启策略来共同保证的。</p>
<p>Go 将自己定位为一个用于构建云原生应用的“零件”，而非一个大包大揽的“一体机”。这种对运维环境的<strong>隐性依赖</strong>，正是其简单性背后的一种深刻权衡。</p>
<h3>“魔鬼在细节中”：切片、接口与错误处理</h3>
<p>Go 的一些核心特性，虽然表面简单，但其底层机制却充满了需要深入理解的“微妙之处”。</p>
<ul>
<li><strong>切片 (Slices)</strong>：新手常常会对其“共享底层数组”的行为感到困惑，不经意间写出因 append 操作导致意外数据修改的 bug。</li>
<li><strong>接口 (Interfaces)</strong>：nil 接口与“值为 nil 的接口”之间的区别，是无数 Gopher 都曾踩过的经典“坑”。</li>
<li><strong>错误处理的冗长</strong>：if err != nil 虽然明确，但在 LLM 辅助编码时代到来之前，这种冗长曾是许多开发者的抱怨之源。现在，新的挑战变成了如何确保依赖 AI 的新手，能真正理解他们生成的每一行错误处理代码。</li>
</ul>
<h2>精通之路——从“知道”到“理解”</h2>
<p>那么，如何跨越从“简单”到“精通”的鸿沟？社区的智慧为我们指明了方向。</p>
<h3>接受 Go 的哲学</h3>
<p>Go 是一门<strong>“刻意设计的简单语言”</strong>。它的目标，是让大型团队能够编写出风格统一、易于阅读和维护的代码。这意味着，你需要接受它的“冗长”，理解它为何抵制某些“高级”特性，并学会在其提供的“约束”下优雅地解决问题。</p>
<h3>刻意练习核心概念</h3>
<p>不要满足于 API 的表面用法。花时间去：</p>
<ul>
<li><strong>画图理解并发模式</strong>：亲自绘制 goroutine 如何通过 channel 通信，理解扇入 (fan-in)、扇出 (fan-out) 等模式。</li>
<li><strong>实验切片的底层行为</strong>：编写小程序来观察 append 何时会触发底层数组的重新分配。</li>
<li><strong>深入标准库源码</strong>：阅读 net/http 或 context 包的源码，是理解 Go 设计哲学的最佳途径。</li>
</ul>
<h3>拥抱“造轮子”</h3>
<p>“你经常需要‘自己动手造轮子’(roll your own)”，一位开发者评论道。这在 Go 的世界里并非贬义。Go 强大的标准库为你提供了高质量的“零件”，鼓励你根据自己的具体需求，组合出最适合的“轮子”，而不是像其他生态那样，总是先去寻找一个庞大、臃肿的“现成汽车”。</p>
<h2>小结：“简单”是起点，而非终点</h2>
<p>回到最初的问题：Go 语言真的简单吗？</p>
<p><strong>是的，Go 的入口极其简单。</strong> 它拥有平缓的学习曲线，让有经验的程序员可以在一周内上手，让新手也能在短时间内构建出有用的程序。</p>
<p><strong>但精通 Go 绝不简单。</strong> 它的真正深度，不在于复杂的语法，而在于理解其并发模型背后的权衡、标准库设计的精妙、以及在简约哲学约束下构建复杂系统的工程智慧。</p>
<p>正如一位评论者所引用的那句古老格言：“<strong>一分钟学会，一辈子精通。</strong>” 虽说“一辈子”有些夸张，但这或许是对 Go 语言简单性与复杂性辩证关系的最佳诠释。Go 的“简单”，为你打开了一扇通往高效、可靠软件工程的大门，但门后的风景，需要你用持续的学习和深刻的思考，去亲自探索和领悟。</p>
<p>资料链接：https://www.reddit.com/r/golang/comments/1oj9jb6/golang_seems_so_simple_am_i_wrong_to_assume_that/</p>
<hr />
<p>你的Go技能，是否也卡在了“熟练”到“精通”的瓶颈期？</p>
<ul>
<li>想写出更地道、更健壮的Go代码，却总在细节上踩坑？</li>
<li>渴望提升软件设计能力，驾驭复杂Go项目却缺乏章法？</li>
<li>想打造生产级的Go服务，却在工程化实践中屡屡受挫？</li>
</ul>
<p>继《<a href="http://gk.link/a/10AVZ">Go语言第一课</a>》后，我的《<a href="http://gk.link/a/12yGY">Go语言进阶课</a>》终于在极客时间与大家见面了！</p>
<p>我的全新极客时间专栏 《<a href="http://gk.link/a/12yGY">Tony Bai·Go语言进阶课</a>》就是为这样的你量身打造！30+讲硬核内容，带你夯实语法认知，提升设计思维，锻造工程实践能力，更有实战项目串讲。</p>
<p>目标只有一个：助你完成从“Go熟练工”到“Go专家”的蜕变！ 现在就加入，让你的Go技能再上一个新台阶！</p>
<p><img src="https://tonybai.com/wp-content/uploads/course-card/iamtonybai-banner-2.gif" alt="" /></p>
<hr />
<p><strong>想系统学习Go，构建扎实的知识体系？</strong></p>
<p>我的新书《<a href="https://book.douban.com/subject/37499496/">Go语言第一课</a>》是你的首选。源自2.4万人好评的极客时间专栏，内容全面升级，同步至Go 1.24。首发期有专属五折优惠，不到40元即可入手，扫码即可拥有这本300页的Go语言入门宝典，即刻开启你的Go语言高效学习之旅！</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/go-primer-published-4.png" alt="" /></p>
<hr />
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求，请扫描下方公众号二维码，与我私信联系。</p>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p style='text-align:left'>&copy; 2025, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2025/11/07/go-simple-illusion-easy-to-learn-hard-to-master/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>致敬 1024 程序员节：写给奔跑在二进制世界里的你 (文末赠书)</title>
		<link>https://tonybai.com/2025/10/24/honoring-1024-programmers-day/</link>
		<comments>https://tonybai.com/2025/10/24/honoring-1024-programmers-day/#comments</comments>
		<pubDate>Fri, 24 Oct 2025 00:09:14 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[0和1]]></category>
		<category><![CDATA[1024程序员节]]></category>
		<category><![CDATA[AccompanyingCode]]></category>
		<category><![CDATA[AIEra]]></category>
		<category><![CDATA[AI时代]]></category>
		<category><![CDATA[BookErrata]]></category>
		<category><![CDATA[Bug]]></category>
		<category><![CDATA[BusinessCooperation]]></category>
		<category><![CDATA[Clanguage]]></category>
		<category><![CDATA[ConcurrentProgramming]]></category>
		<category><![CDATA[CoreCompetitiveness]]></category>
		<category><![CDATA[C语言]]></category>
		<category><![CDATA[deadlock]]></category>
		<category><![CDATA[DoubleElevenPromotion]]></category>
		<category><![CDATA[EcommercePlatform]]></category>
		<category><![CDATA[err]]></category>
		<category><![CDATA[github]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[GoLanguage]]></category>
		<category><![CDATA[Go语言]]></category>
		<category><![CDATA[Go语言第一课]]></category>
		<category><![CDATA[nil]]></category>
		<category><![CDATA[SignedEdition]]></category>
		<category><![CDATA[SystematicLearning]]></category>
		<category><![CDATA[TonyBai]]></category>
		<category><![CDATA[二进制世界]]></category>
		<category><![CDATA[代码]]></category>
		<category><![CDATA[公众号]]></category>
		<category><![CDATA[创造力]]></category>
		<category><![CDATA[双十一促销]]></category>
		<category><![CDATA[商务合作]]></category>
		<category><![CDATA[图书勘误]]></category>
		<category><![CDATA[并发编程]]></category>
		<category><![CDATA[极客时间专栏]]></category>
		<category><![CDATA[核心竞争力]]></category>
		<category><![CDATA[死锁]]></category>
		<category><![CDATA[电商平台]]></category>
		<category><![CDATA[签名版]]></category>
		<category><![CDATA[系统性学习]]></category>
		<category><![CDATA[编译]]></category>
		<category><![CDATA[配套代码]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=5300</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2025/10/24/honoring-1024-programmers-day 大家好，我是Tony Bai。 今天，10 月 24 日，是一个特殊的日子。 它并非法定假日，地图上也没有标注。但对于一群特定的人来说，这个日期本身，就是一种无需言说的默契。1024，是 2 的 10 次方，是 1KB，是我们构建整个数字世界的基石。 它，就是属于我们程序员自己的节日——1024 程序员节。 所以，今天这篇文章，不聊源码，不谈架构，只想写给每一个奔跑在二进制世界里的你： 致敬那些深夜里，与 Bug 搏斗到天明的执着身影； 致敬那些显示器前，在 0 和 1 中创造出无限可能的大脑； 致敬那些用一行行代码，默默改变着世界的同行者们。 你们，值得被看见，被理解，被尊重。 程序员的宿命：永远在学习“第一课” 作为程序员，我们的职业生涯，似乎就是一场永无止境的“学习第一课”的旅程。 我至今仍记得自己第一次学习 C 语言时，面对指针的困惑；第一次接触并发编程时，被死锁折磨的痛苦；第一次探索 Go 语言时，被其简单哲学所震撼的喜悦。 无论是学习一门新语言，还是掌握一个新框架，亦或是理解一种新的架构思想，我们总是在不断地“清空自己”，以一个初学者的心态，回到“第一课”的起点。 这正是这个职业最磨人、也最迷人的地方。它强迫我们保持好奇，持续奔跑，永不僵化。 我将我的极客时间专栏《Go语言第一课》沉淀成书，正是源于对这份“程序员宿命”的深刻理解。我希望它不仅仅是教你一门语言的语法，更是想为你提供一套坚实的、可信赖的、能够举一反三的学习体系和思维范式。它是我作为一个“长期主义”布道者，希望能为你的下一段“第一课”之路，铺下的一块最坚固的基石。 灵魂拷问：AI 时代，我们还需要“第一课”吗？ 我知道，很多人心里都有一个疑问：在 AI 如此强大的今天，我们似乎可以随时跳过所有“第一课”，直接向 AI 要答案。那么，系统性的学习是否已经过时？ 作为一名同样深度使用 AI 的工程师，我的答案是：不，恰恰相反，在这个时代，扎实的“第一课”比以往任何时候都更加重要。 AI 是“陪练”，不是“内功心法”。 它可以极大地加速我们实现想法的过程，但它无法替代我们建立知识体系的“内功”修炼。它能告诉你“是什么”，却很少能告诉你“为什么”。 我看到太多的初级工程师，在 AI 带来的“我什么都行”的幻觉中，陷入了“知其然，不知其所以然”的困境。这种“能力空心化”，会在未来的某个时刻，成为职业生涯中难以逾越的瓶颈。 而系统性地学习一本好的入门书，正是在 [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/2025/honoring-1024-programmers-day-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2025/10/24/honoring-1024-programmers-day">本文永久链接</a> &#8211; https://tonybai.com/2025/10/24/honoring-1024-programmers-day</p>
<p>大家好，我是Tony Bai。</p>
<p>今天，10 月 24 日，是一个特殊的日子。</p>
<p>它并非法定假日，地图上也没有标注。但对于一群特定的人来说，这个日期本身，就是一种无需言说的默契。<strong>1024</strong>，是 2 的 10 次方，是 1KB，是我们构建整个数字世界的基石。</p>
<p>它，就是属于我们程序员自己的节日——<strong>1024 程序员节</strong>。</p>
<p>所以，今天这篇文章，不聊源码，不谈架构，只想写给每一个奔跑在二进制世界里的你：</p>
<ul>
<li>致敬那些深夜里，与 Bug 搏斗到天明的执着身影；</li>
<li>致敬那些显示器前，在 0 和 1 中创造出无限可能的大脑；</li>
<li>致敬那些用一行行代码，默默改变着世界的同行者们。</li>
</ul>
<p><strong>你们，值得被看见，被理解，被尊重。</strong></p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/paid/go-network-programming-complete-guide-pr.png" alt="" /></p>
<h2>程序员的宿命：永远在学习“第一课”</h2>
<p>作为程序员，我们的职业生涯，似乎就是一场永无止境的“学习第一课”的旅程。</p>
<p>我至今仍记得自己第一次学习 C 语言时，面对指针的困惑；第一次接触并发编程时，被死锁折磨的痛苦；第一次探索 Go 语言时，被其简单哲学所震撼的喜悦。</p>
<p>无论是学习一门新语言，还是掌握一个新框架，亦或是理解一种新的架构思想，我们总是在不断地“清空自己”，以一个初学者的心态，回到“第一课”的起点。</p>
<p>这正是这个职业<strong>最磨人、也最迷人的地方</strong>。它强迫我们<strong>保持好奇，持续奔跑，永不僵化</strong>。</p>
<p>我将我的极客时间专栏<a href="https://tonybai.com/2025/08/28/go-primer-published">《Go语言第一课》沉淀成书</a>，正是源于对这份“程序员宿命”的深刻理解。我希望它不仅仅是教你一门语言的语法，更是想为你提供一套<strong>坚实的、可信赖的、能够举一反三的学习体系和思维范式</strong>。它是我作为一个“长期主义”布道者，希望能为你的下一段“第一课”之路，铺下的一块最坚固的基石。</p>
<h2>灵魂拷问：AI 时代，我们还需要“第一课”吗？</h2>
<p>我知道，很多人心里都有一个疑问：在 AI 如此强大的今天，我们似乎可以随时跳过所有“第一课”，直接向 AI 要答案。那么，<a href="https://tonybai.com/2025/04/19/learn-go-in-ai-era">系统性的学习是否已经过时</a>？</p>
<p>作为一名同样深度使用 AI 的工程师，我的答案是：<strong>不，恰恰相反，在这个时代，扎实的“第一课”比以往任何时候都更加重要。</strong></p>
<p><strong>AI 是“陪练”，不是“内功心法”。</strong> 它可以极大地加速我们实现想法的过程，但它无法替代我们建立知识体系的“内功”修炼。它能告诉你“是什么”，却很少能告诉你“为什么”。</p>
<p>我看到太多的<a href="https://tonybai.com/2025/08/24/junior-engineer-survival-guide-in-ai-age/">初级工程师，在 AI 带来的“我什么都行”的幻觉中</a>，陷入了“知其然，不知其所以然”的困境。这种“能力空心化”，会在未来的某个时刻，成为职业生涯中难以逾越的瓶颈。</p>
<p>而系统性地学习一本好的入门书，正是在 AI 时代对抗这种“能力空心化”、构建自己不可替代核心竞争力的最佳途径。它强迫你去理解代码背后的设计哲学、核心原理和权衡取舍，而这些，恰恰是 AI 无法生成的、属于你自己的智慧。</p>
<h2>节日献礼：送你一本签名的《Go语言第一课》！</h2>
<p>在这个属于我们自己的节日里，我想用一份最“硬核”的礼物，来回馈大家一直以来的支持，也为每一位仍在奔跑的同行者，加一次油，充一次电。</p>
<p>我准备了 <strong>2 本</strong>我的<strong>亲笔签名版《Go语言第一课》</strong>，送给我的读者们。</p>
<p><strong>【参与方式】</strong></p>
<p><a href="https://mp.weixin.qq.com/s/pmDbFhP13QIEee_qZznM_w">点击此链接</a>进入我的公众号文章，分享文章，转发朋友圈，并在本文评论区<strong>留言</strong>，<strong>说说你作为程序员最难忘的一个瞬间/故事</strong>，或者<strong>你对程序员这个职业最深的思考</strong>。</p>
<p>它可以是一次通宵排查 Bug 后的豁然开朗，可以是自己的代码被千万用户使用时的成就感，也可以是对这个行业未来的迷茫与期许。</p>
<p><strong>【抽奖规则】</strong></p>
<p>我将从所有留言中，精选 <strong>2 条</strong>最走心、最能打动我的分享，每人赠送一本我的<strong>亲笔签名版《Go语言第一课》</strong>！</p>
<p><strong>【活动截止时间】</strong></p>
<p><strong>2025年10月31日 23:59</strong></p>
<p>期待在留言区，看到你的故事。</p>
<h2>行动号召：为你的热爱，充一次电！</h2>
<p>当然，节日的福利属于每一个人。</p>
<p>如果你不想等待抽奖，或者想把这份礼物送给身边正在学习 Go 的朋友，现在就是最好的时机。双十一促销已经启动，各大电商平台的五折购书折扣都是全年最低。<strong>不到 40 元</strong>，即可拥有这本经过 2.4w 人验证、300 多页的 Go 入门宝典。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/go-primer-published-4.png" alt="" /></p>
<ul>
<li><strong>图书勘误与配套代码</strong>：https://github.com/bigwhite/goprimer</li>
</ul>
<h2>小结：愿我们永远奔跑</h2>
<p>最后，再次向每一位奔跑在二进制世界里的同行者致敬。</p>
<p>愿你的代码永远优雅，愿你的编译永远通过，愿你的创造力永不枯竭，愿你的 err 永远为 nil。</p>
<p><strong>1024，程序员节快乐！</strong></p>
<hr />
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求，请扫描下方公众号二维码，与我私信联系。</p>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p style='text-align:left'>&copy; 2025, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2025/10/24/honoring-1024-programmers-day/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Java屹立30年，Go的“少年壮志”如何续写辉煌？——来自Java之父的“长寿秘诀”</title>
		<link>https://tonybai.com/2025/05/17/java-at-30/</link>
		<comments>https://tonybai.com/2025/05/17/java-at-30/#comments</comments>
		<pubDate>Sat, 17 May 2025 00:35:32 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[AI]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[GenAI]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[go1.18]]></category>
		<category><![CDATA[go1兼容性]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[Gopher]]></category>
		<category><![CDATA[JamesGosling]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Java之父]]></category>
		<category><![CDATA[NewStack]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[云原生]]></category>
		<category><![CDATA[人工智能]]></category>
		<category><![CDATA[元宇宙]]></category>
		<category><![CDATA[分布式系统]]></category>
		<category><![CDATA[区块链]]></category>
		<category><![CDATA[向后兼容]]></category>
		<category><![CDATA[多核时代]]></category>
		<category><![CDATA[工程效率]]></category>
		<category><![CDATA[并发编程]]></category>
		<category><![CDATA[标准库]]></category>
		<category><![CDATA[泛型]]></category>
		<category><![CDATA[简洁性]]></category>
		<category><![CDATA[错误处理]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=4722</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2025/05/17/java-at-30 大家好，我是Tony Bai。我的极客时间《Go进阶课》专栏已经上线，欢迎大家点击链接订阅学习，我们一起在Go语言的道路上共同精进！ Go语言自开源以来，已走过十多个年头。从最初备受瞩目的“Google语言”，到如今在云原生、微服务领域独当一面，Go 凭借其简洁、高效与强大的并发能力，赢得了全球开发者的青睐，正从一个朝气蓬勃的少年”迈向更加成熟稳健的“壮年”。 然而，“成长的烦恼”也随之而来：生态如何持续繁荣？语言如何在保持核心优势与满足新兴需求之间取得平衡？如何应对一波又一波的技术浪潮冲击？ 恰逢 Java 语言诞生 30 周年，The New Stack 对 Java 之父 James Gosling 进行了一次深度访谈。我刚接触 Java 时，它才发布 1.5 版本（Tiger），一晃近 20 年，Java 依然是全球最重要的语言之一。这位编程语言界的“老大哥”和它的创造者，其“长寿秘诀”无疑能为“风华正茂”的 Go 语言带来诸多启示。 Gosling 在访谈中分享了 Java 长盛不衰的关键，我提炼了几点，希望能为Go的未来之路提供一些借鉴与思考。 秘诀一：【解决真实问题，而非追逐时髦】—— Go 的初心与未来挑战 Java 的经验： James Gosling 强调：“Java 从不追求时髦，始终专注于有效解决问题，帮助工程师完成工作。” 这份对实用主义的坚守，是 Java 能够穿越多个技术周期的基石。 Go 的启示与思考： Go 语言的诞生，正是为了解决当时 C++ 开发的复杂性、Python 等脚本语言的性能瓶颈以及多核时代并发编程的困境。它以大道至简的哲学，直击痛点，迅速在云原生、分布式系统等领域找到了自己的核心价值。 如今，Go 已走过开源的第一个十年，生态日渐成熟。面对 [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/2025/java-at-30-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2025/05/17/java-at-30">本文永久链接</a> &#8211; https://tonybai.com/2025/05/17/java-at-30</p>
<p>大家好，我是Tony Bai。<a href="http://gk.link/a/12yGY">我的极客时间《Go进阶课》专栏已经上线</a>，欢迎大家<a href="http://gk.link/a/12yGY">点击链接</a>订阅学习，我们一起在Go语言的道路上共同精进！</p>
<p><a href="https://tonybai.com/2024/11/12/go-turns-15">Go语言自开源以来，已走过十多个年头</a>。从最初备受瞩目的“Google语言”，到如今在云原生、微服务领域独当一面，Go 凭借其简洁、高效与强大的并发能力，赢得了全球开发者的青睐，正从一个朝气蓬勃的少年”迈向更加成熟稳健的“壮年”。</p>
<p>然而，“成长的烦恼”也随之而来：生态如何持续繁荣？语言如何在保持核心优势与满足新兴需求之间取得平衡？如何应对一波又一波的技术浪潮冲击？</p>
<p>恰逢 Java 语言诞生 30 周年，<a href="https://thenewstack.io/java-at-30-the-genius-behind-the-code-that-changed-tech/">The New Stack 对 Java 之父 James Gosling 进行了一次深度访谈</a>。我刚接触 Java 时，它才发布 1.5 版本（Tiger），一晃近 20 年，Java 依然是全球最重要的语言之一。这位编程语言界的“老大哥”和它的创造者，其“长寿秘诀”无疑能为“风华正茂”的 Go 语言带来诸多启示。</p>
<p>Gosling 在访谈中分享了 Java 长盛不衰的关键，我提炼了几点，希望能为Go的未来之路提供一些借鉴与思考。</p>
<h2>秘诀一：【解决真实问题，而非追逐时髦】—— Go 的初心与未来挑战</h2>
<ul>
<li><strong>Java 的经验：</strong> James Gosling 强调：“Java 从不追求时髦，始终专注于有效解决问题，帮助工程师完成工作。” 这份对实用主义的坚守，是 Java 能够穿越多个技术周期的基石。</li>
<li><strong>Go 的启示与思考：</strong> Go 语言的诞生，正是为了解决当时 C++ 开发的复杂性、Python 等脚本语言的性能瓶颈以及多核时代并发编程的困境。它以大道至简的哲学，直击痛点，迅速在云原生、分布式系统等领域找到了自己的核心价值。</li>
</ul>
<p>如今，Go 已走过开源的第一个十年，生态日渐成熟。面对 AI 浪潮、不断演进的硬件架构以及更多元化的应用场景，Go 是否还能保持这份“解决真实问题”的初心？未来，Go 需要识别并解决哪些新的、关键的“真实问题”，以巩固和拓展自身的生态位？这是每一个 Gopher 和 Go 社区贡献者都需要思考的。</p>
<h2>秘诀二：【尊重用户，死磕向后兼容与可靠性】—— Go 的生命线如何延续？</h2>
<ul>
<li><strong>Java 的经验：</strong> “尊重用户”、“保持向后兼容”、“优先考虑可靠性 (必须每次都TM的能用！)”——Gosling 的这些话掷地有声，道出了 Java 赢得企业信任的关键。</li>
<li><strong>Go 的启示与思考：</strong> Go 语言著名的“Go 1 兼容性承诺”为其赢得了极佳的口碑，让开发者能够放心地升级版本。然而，随着 Go Modules、泛型等重要特性的引入，社区中也出现了一些关于“必要妥协”与“更大收益”的讨论。</li>
</ul>
<p>当 Go 生态越来越庞大，用户场景越来越复杂时，如何在不牺牲核心稳定性的前提下，引入必要的改进和演化？如何在社区对某些“破坏性但可能带来更大价值”的变革呼声与“向后兼容”的承诺之间找到最佳平衡点？这将持续考验 Go 核心团队的智慧和社区的共识。</p>
<h2>秘诀三：【谨慎创新，不轻易破坏核心价值】—— Go 的“简洁”能否持久？</h2>
<ul>
<li><strong>Java 的经验：</strong> 谈及 Lambda、泛型等重要特性时，Gosling 表示：“我从不想加入一个不‘对’的东西。”他坦言确定最佳实现方式的艰难（“最初的90%很容易想出来，但最后10%超级难”）。这种对语言核心价值的审慎态度，避免了 Java 过早地陷入复杂性的泥潭。</li>
<li><strong>Go 的启示与思考：</strong> Go 在引入泛型时，同样经历了长达数年的社区讨论和极其审慎的设计过程，最终才在 <a href="https://tonybai.com/2022/04/20/some-changes-in-go-1-18">Go 1.18 中落地</a>。这种“慢”在某种程度上保证了新特性与 Go 整体设计哲学的融合。</li>
</ul>
<p>未来，Go 必然会面临更多引入新特性的需求。如何在满足发展需要与保持语言核心的“简洁性”和“工程效率”之间取得平衡，避免语言的“膨胀”和“复杂化”，将是 Go 能否保持其独特魅力的关键。我们是否应该继续推崇“少即是多”？哪些领域的创新是必要的，哪些又是需要警惕的？</p>
<h2>秘诀四：【社区是活水之源】—— Go 的生态如何更上一层楼？</h2>
<ul>
<li><strong>Java 的经验：</strong> 即便在 Oracle 的管理下（Gosling 对 Oracle 的评价是“比预想的好，但期望本来就很低”），他也承认社区在 Java 持续发展和创新中扮演了至关重要的角色。</li>
<li><strong>Go 的启示与思考：</strong> Go 拥有一个全球化、充满活力且贡献卓著的社区。从无数优秀的开源项目到各种技术峰会、meetup，社区的力量是 Go 快速成长的重要驱动力。</li>
</ul>
<p>进入成熟期后，如何进一步赋能社区，形成更强大的合力？例如，在语言特性方面(如<a href="https://tonybai.com/2025/02/08/personal-idea-about-using-question-mark-operator-in-go-error-handling-new-proposal/">更完善的错误处理</a>、更丰富的原生数据结构支持等，虽然泛型已带来一些改善），如何更好地组织和激励社区进行共建？如何让更多的企业和个人开发者参与到 Go 的核心贡献和生态治理中？这将是 Go 能否持续保持创新活力的关键。</p>
<h2>秘诀五：【保持清醒的自我定位，警惕技术炒作】—— Go 在浪潮中的定力</h2>
<ul>
<li><strong>Java 的经验：</strong> Gosling 对当前 AI 热潮的一些犀利点评（例如，称其为“自带一桶有毒废料的营销术语”，认为“大部分 AI 投资将化为乌有”，以及 AI 编码工具在复杂项目中“几乎总是会崩溃”）展现了一种宝贵的清醒和批判精神。他认为 AI 更多是“极其复杂的锤子和螺丝刀”，是人类使用的工具，而非取代人类的自主系统。</li>
<li><strong>Go 的启示与思考：</strong> 面对一波又一波的新兴技术浪潮（从区块链到元宇宙，再到如今的生成式 AI），Go 语言需要有清晰的自我认知和战略定力。它在技术栈中的核心价值是什么？最适合解决哪些领域的问题？</li>
</ul>
<p>Go 在并发处理、网络编程、系统构建方面的优势，使其在云原生、微服务、分布式系统以及 AI 应用的后端基础设施等领域大放异彩。未来，Go 如何在这些领域继续深耕，同时审慎地探索与新兴技术的结合点，而不是被短期热点裹挟，盲目扩张，这将考验 Go 社区的集体智慧。</p>
<h2>小结：知易行难，未来可期</h2>
<p>James Gosling 的访谈，不仅仅是对 Java 30 年的回顾，更像是一堂浓缩的技术发展史和语言设计哲学课。虽然很多“名言警句”（比如他对某些公司文化的吐槽，或者对过时工具的调侃）非常抓人眼球，但其背后对技术本质的坚守、对用户价值的尊重、以及对行业趋势的冷静洞察，或许才是 Java 能够穿越周期、屹立 30 年的深层原因。</p>
<p>Go 语言，这位“刚刚十多岁的少年”，正处在发展的黄金时期，也面临着成长的关键抉择。借鉴 Java 这位“老大哥”的宝贵经验，或许能帮助 Go 走得更稳、更远。</p>
<p><strong>想更全面了解 James Gosling 的观点和那些有趣的编程往事吗？推荐阅读原文：</strong><br />
<a href="https://thenewstack.io/java-at-30-the-genius-behind-the-code-that-changed-tech/">Java at 30: The Genius Behind the Code That Changed Tech</a></p>
<hr />
<p><strong>聊一聊，聚焦 Go 的未来：</strong></p>
<ul>
<li><strong>你认为当前 Go 语言发展面临的最大挑战是什么（技术层面或生态层面）？</strong></li>
<li><strong>借鉴 Java 的经验，你觉得 Go 社区或官方在哪些方面可以做得更好，以确保其长期健康发展？</strong></li>
<li><strong>对于 Go 语言的核心价值（如简洁、并发、工程效率），你认为在未来演进中最需要坚守的是什么？</strong></li>
</ul>
<p>欢迎大家围绕 Go 的未来发展，在<strong>评论区</strong>分享你的深度思考！如果你觉得这篇文章对你有所启发，也请<strong>转发给你身边的 Gopher 朋友们</strong>，让我们一起为 Go 的未来出谋划策。</p>
<p><strong>想与我进行更深入的 Go 语言与 AI 技术交流吗？</strong> 欢迎加入我的<strong>“Go &amp; AI 精进营”知识星球</strong>。</p>
<p><img src="http://image.tonybai.com/img/tonybai/gopher-and-ai-tribe-zsxq-small-card.jpg" alt="img{512x368}" /></p>
<p>我们星球见！</p>
<hr />
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求，请扫描下方公众号二维码，与我私信联系。</p>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p style='text-align:left'>&copy; 2025, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2025/05/17/java-at-30/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Go unique包：突破字符串局限的通用值Interning技术实现</title>
		<link>https://tonybai.com/2024/09/18/understand-go-unique-package-by-example/</link>
		<comments>https://tonybai.com/2024/09/18/understand-go-unique-package-by-example/#comments</comments>
		<pubDate>Tue, 17 Sep 2024 22:06:19 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[GC]]></category>
		<category><![CDATA[generics]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[go1.23]]></category>
		<category><![CDATA[go4org]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[goroutine]]></category>
		<category><![CDATA[Go标准库]]></category>
		<category><![CDATA[Handle]]></category>
		<category><![CDATA[hash]]></category>
		<category><![CDATA[json]]></category>
		<category><![CDATA[Lisp]]></category>
		<category><![CDATA[Make]]></category>
		<category><![CDATA[map]]></category>
		<category><![CDATA[netaddr]]></category>
		<category><![CDATA[Package]]></category>
		<category><![CDATA[runtime]]></category>
		<category><![CDATA[std]]></category>
		<category><![CDATA[strong]]></category>
		<category><![CDATA[tailscale]]></category>
		<category><![CDATA[unique]]></category>
		<category><![CDATA[value]]></category>
		<category><![CDATA[weak]]></category>
		<category><![CDATA[XML]]></category>
		<category><![CDATA[值比较]]></category>
		<category><![CDATA[内存]]></category>
		<category><![CDATA[内存优化]]></category>
		<category><![CDATA[内存管理]]></category>
		<category><![CDATA[哈希表]]></category>
		<category><![CDATA[字符串]]></category>
		<category><![CDATA[字符串interning]]></category>
		<category><![CDATA[并发编程]]></category>
		<category><![CDATA[弱指针]]></category>
		<category><![CDATA[强指针]]></category>
		<category><![CDATA[数据结构]]></category>
		<category><![CDATA[方法]]></category>
		<category><![CDATA[泛型]]></category>
		<category><![CDATA[运行时]]></category>
		<category><![CDATA[锁]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=4283</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2024/09/18/understand-go-unique-package-by-example Go的1.23版本中引入了一个新的标准库包unique，为Go开发者带来了高效的值interning能力。这种能力不仅适用于字符串类型值，还可应用于任何可比较(comparable)类型的值。 本文将简要探讨interning技术及其在Go中的实现方式，通过介绍unique包的功能，帮助读者更好地理解这一技术及其实际应用。 1. 从string interning技术说起 通常提到interning技术时，指的是传统的字符串驻留（string interning）技术。它是一种优化方法，旨在减少程序中重复字符串的内存占用，并提高字符串比较操作的效率。其基本原理是将相同的字符串值在内存中只存储一次，所有对该字符串的引用都指向同一内存地址，而不是为每个相同字符串创建单独的副本。下图展示了使用和不使用string interning技术的对比: 这个图直观地展示了string interning如何通过共享相同的字符串来节省内存和提高效率。我们看到：在不使用string interning的情况下，每个字符串都有自己的内存分配，即使内容相同，比如”Hello”字符串出现两次，占用了两块不同的内存空间。而在使用string interning的情况下，相同内容的字符串只存储一次，比如：两个”Hello”字符串引用指向同一个内存位置。 string interning在多种场景下非常有用，比如在解析文本格式(如XML、JSON)时，interning能高效处理标签名称经常重复的问题；在编译器或解释器的实现时，interning能够减少符号表中的重复项等。 传统的string interning通常使用哈希表或字典来存储字符串的唯一实例。每次出现新字符串时，程序首先会检查哈希表中是否已有相同的字符串，若存在则返回其引用，若不存在则将其存储在表中。 Michael Knyszek在Go官博介绍interning技术时，也给出了一个传统实现的代码片段： var internPool map[string]string // Intern returns a string that is equal to s but that may share storage with // a string previously passed to Intern. func Intern(s string) string { pooled, ok := [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/understand-go-unique-package-by-example-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2024/09/18/understand-go-unique-package-by-example">本文永久链接</a> &#8211; https://tonybai.com/2024/09/18/understand-go-unique-package-by-example</p>
<p><a href="https://tonybai.com/2024/08/19/some-changes-in-go-1-23">Go的1.23版本</a>中引入了一个<a href="https://pkg.go.dev/unique?ref=tonybai.com">新的标准库包unique</a>，为Go开发者带来了高效的值<a href="https://en.wikipedia.org/wiki/Interning_(computer_science)">interning能力</a>。这种能力不仅适用于字符串类型值，还可应用于任何可比较(comparable)类型的值。</p>
<p>本文将简要探讨interning技术及其在Go中的实现方式，通过介绍unique包的功能，帮助读者更好地理解这一技术及其实际应用。</p>
<h2>1. 从string interning技术说起</h2>
<p>通常提到interning技术时，指的是传统的字符串驻留（string interning）技术。它是一种优化方法，旨在<strong>减少程序中重复字符串的内存占用</strong>，并<strong>提高字符串比较操作的效率</strong>。其基本原理是将相同的字符串值在内存中只存储一次，所有对该字符串的引用都指向同一内存地址，而不是为每个相同字符串创建单独的副本。下图展示了使用和不使用string interning技术的对比:</p>
<p><img src="https://tonybai.com/wp-content/uploads/understand-go-unique-package-by-example-2.png" alt="" /></p>
<p>这个图直观地展示了string interning如何通过共享相同的字符串来节省内存和提高效率。我们看到：在不使用string interning的情况下，每个字符串都有自己的内存分配，即使内容相同，比如”Hello”字符串出现两次，占用了两块不同的内存空间。而在使用string interning的情况下，相同内容的字符串只存储一次，比如：两个”Hello”字符串引用指向同一个内存位置。</p>
<p>string interning在多种场景下非常有用，比如在解析文本格式(如XML、JSON)时，interning能高效处理标签名称经常重复的问题；在编译器或解释器的实现时，interning能够减少符号表中的重复项等。</p>
<p>传统的string interning通常使用哈希表或字典来存储字符串的唯一实例。每次出现新字符串时，程序首先会检查哈希表中是否已有相同的字符串，若存在则返回其引用，若不存在则将其存储在表中。</p>
<p>Michael Knyszek在<a href="https://go.dev/blog/unique?ref=tonybai.com">Go官博介绍interning技术</a>时，也给出了一个传统实现的代码片段：</p>
<pre><code>var internPool map[string]string

// Intern returns a string that is equal to s but that may share storage with
// a string previously passed to Intern.
func Intern(s string) string {
    pooled, ok := internPool[s]
    if !ok {
        // Clone the string in case it's part of some much bigger string.
        // This should be rare, if interning is being used well.
        pooled = strings.Clone(s)
        internPool[pooled] = pooled
    }
    return pooled
}
</code></pre>
<p>这种实现虽然简单，但Knyszek指出了其存在几个问题：</p>
<ul>
<li>一旦字符串被intern，就永远不会被释放。</li>
<li>在多goroutine环境下使用需要额外的同步机制。</li>
<li>仅限于字符串类型值，不能用于其他类型的值。</li>
</ul>
<p>Go 1.23版本引入的unique包就是string interning技术的一种Go官方实现，当然就像前面所说，unique包不仅仅支持传统的string interning，还支持任何支持比较的类型的值的interning。</p>
<p>不过，在介绍unique包之前，我们简单看看这些年来Go社区对interning技术的贡献。</p>
<h2>2. Go社区interning技术的实现简史</h2>
<p>由于其他主流语言都或多或少有了对string interning的支持，Go社区显然也需要这样的包，在Go issues列表中，我能找到的最早提出在Go中添加interning技术实现的是2013年go核心开发人员Brad Fitzpatrick提出的”<a href="https://github.com/golang/go/issues/5160?ref=tonybai.com">proposal: runtime: optionally allow callers to intern strings</a>“。</p>
<p>2019年，Josh Bleecher Snyder发表了一篇博文<a href="https://commaok.xyz/post/intern-strings?ref=tonybai.com">Interning strings in Go</a>，探讨了interning的Go实现方法，并给出一个<a href="https://github.com/josharian/intern">简单但重度使用sync.Pool的interning实现</a>，该实现支持对string和字节切片的interning。</p>
<p>2021年，tailscale为了实现<a href="https://github.com/inetaf/netaddr">可以高效表示ip地址的netaddr包</a>，构建和开源了<a href="https://github.com/go4org/intern">go4.org/intern包</a>，这是一个可用于量产级别的interning实现。</p>
<blockquote>
<p>注：go4.org中这个go4的名字很可能就是因为go4.org这个组织只有四个contributors：Brad Fitzpatrick、Josh Bleecher Snyder、Dave Anderson和Matt Layher。之前的一篇文章《<a href="https://tonybai.com/2023/04/16/understanding-unsafe-assume-no-moving-gc/">理解unsafe-assume-no-moving-gc包</a>》中的unsafe-assume-no-moving-gc包也是go4.org下面的。</p>
</blockquote>
<p>之后，Brad Fitzpatrick将inetaf/netaddr包的实现合并到了Go标准库net/netip中，而netaddr包依赖的go4.org/intern包也被移入Go项目，变为internal/intern包，并被net/netip包所使用。</p>
<p>直到2023年9月，mknyszek提出”<a href="https://github.com/golang/go/issues/62483">unique: new package with unique.Handle</a>“的proposal，给出unique包的API设计和参考实现。unique落地后，原先使用internal/intern包的net/netip也都改为使用unique包了，internal/intern在Go 1.23版本被移除。</p>
<p>接下来，我们来看看这篇文章的主角unique包。</p>
<h2>3. Go的unique包介绍</h2>
<p>相较于传统的interning实现以及Go社区之前的实现，Go 1.23引入的unique包提供了一个更加通用和高效的interning实现方案。下面我们就分别从API、unique包的优势以及实现原理等几个方面介绍一下这个包。</p>
<h3>3.1 unique包的API</h3>
<p>从用户角度看，unique包提供的核心API非常简洁：</p>
<pre><code>$go doc unique.Handle
package unique // import "unique"

type Handle[T comparable] struct {
    // Has unexported fields.
}

func Make[T comparable](value T) Handle[T]
func (h Handle[T]) Value() T
</code></pre>
<p>Make函数就是unique包的”Intern”函数，它接受一个可比较类型的值，返回一个intern后的值，不过和前面那个传统实现方式的Intern函数不同，Make函数返回的是一个Handle&#91;T&#93;类型的值。针对同一个传给Make函数的值，返回的Handle&#91;T&#93;类型的值是相同的：</p>
<pre><code>// unique-examples/string_interning.go
package main

import "unique"

func main() {
    h1 := unique.Make("hello")
    h2 := unique.Make("hello")
    h3 := unique.Make("hello")
    h4 := unique.Make("golang")
    println(h1 == h2) // true
    println(h1 == h3) // true
    println(h1 == h4) // false
    println(h2 == h4) // false
}
</code></pre>
<p>unique包的作者Knyszek认为Handle&#91;T&#93;和Lisp语言中的Symbol十分类似，Symbol在Lisp中是interned后的字符串，Lisp确保相同的字符串只存储一次，提高内存存储和使用效率。</p>
<p>不过前面说了，unique不仅支持字符串值的interning，还支持其他可比较类型的值的interning，下面是一个int interning和一个自定义可比较类型的interning的例子：</p>
<pre><code>// unique-examples/int_interning.go

package main

import "unique"

func main() {
    var a, b int = 5, 6
    h1 := unique.Make(a)
    h2 := unique.Make(a)
    h3 := unique.Make(b)
    println(h1 == h2) // true
    println(h1 == h3) // false
}

// unique-examples/user_type_interning.go

package main

import "unique"

type UserType struct {
    a int
    z float64
    s string
}

func main() {
    var u1 = UserType{
        a: 5,
        z: 3.14,
        s: "golang",
    }
    var u2 = UserType{
        a: 5,
        z: 3.15,
        s: "golang",
    }
    h1 := unique.Make(u1)
    h2 := unique.Make(u1)
    h3 := unique.Make(u2)
    println(h1 == h2) // true
    println(h1 == h3) // false
}
</code></pre>
<blockquote>
<p>注：如果要intern的类型T是包含指针的结构体，这些指针指向的值几乎总是会逃逸到堆上。</p>
</blockquote>
<p>通过Make获得的Handle&#91;T&#93;的Value方法可以获取到interning值的原始值，我们看下面示例：</p>
<pre><code>// unique-examples/value.go
package main

import (
    "fmt"
    "unique"
)

type UserType struct {
    a int
    z float64
    s string
}

func main() {
    var u1 = UserType{
        a: 5,
        z: 3.14,
        s: "golang",
    }
    h1 := unique.Make(u1)
    h2 := unique.Make("hello, golang")
    h3 := unique.Make(567890)
    v1 := h1.Value()
    v2 := h2.Value()
    v3 := h3.Value()
    fmt.Printf("%T: %v\n", v1, v1) // main.UserType: {5 3.14 golang}
    fmt.Printf("%T: %v\n", v2, v2) // string: hello, golang
    fmt.Printf("%T: %v\n", v3, v3) // int: 567890
}
</code></pre>
<blockquote>
<p>注：Value方法返回的是值的浅拷贝，对于复合类型可能存在共享底层数据的情况。</p>
</blockquote>
<h3>3.2 unique包的实现原理</h3>
<p>传统的字符串interning实现起来可能并不难，但unique包的目标是设计支持可比较类型、interning值也可被GC且支持快速interning值比较的方案，unique包的实现涉及到hashtrimap、细粒度锁以及与runtime内gc相关函数结合的技术难题，因此其门槛还是很高的，即便是Go核心团队成员Knyszek实现的unique包，在Go 1.23发布后也被发现了<a href="https://github.com/golang/go/issues/69370">较为“严重”的bug</a>，该问题将<a href="https://github.com/golang/go/issues/69383">在Go 1.23.2版本修正</a>。</p>
<p>下面是一个unique包实现原理的示意图：</p>
<p><img src="https://tonybai.com/wp-content/uploads/understand-go-unique-package-by-example-3.png" alt="" /></p>
<p>上图展示了Make、Handle&#91;T&#93;和Value方法之间的关系，以及它们如何与内部的map(hashtrieMap)交互。</p>
<p>我们看到，图中三次调用Make(“hello”)都返回相同的Handle&#91;string&#93;{ptr1}，即无论调用多少次Make，对于相同的输入值，Make总是返回相同的Handle。</p>
<p>图中的Handle&#91;string&#93;{ptr1}是一个包含指向存储”hello”的内存位置指针的结构，所有三次Make调用返回的Handle都指向同一个内存位置。下面是Handle结构体的定义，看了你就明白了这句话的含义：</p>
<pre><code>// $GOROOT/src/unique/handle.go
type Handle[T comparable] struct {
    value *T
}
</code></pre>
<blockquote>
<p>注：这里Handle内部的指针&#42;T都是strong pointer(强指针)，以图中示例，只要有一个Handle实例(由Make返回的)存在，内存中的”hello”就不会被GC。</p>
</blockquote>
<p>Handle&#91;string&#93;{ptr1}的Value()方法返回存储的字符串值”hello”。</p>
<p>unique包有一个内部map(hashtrieMap)存储键值对，键是字符串”hello”的clone，值是一个weak.Pointer，指向存储实际字符串值的内存位置。weak.Pointer 是Go 1.23版本的内部包internal/weak中的一个类型，主要用于实现弱指针（weak pointer）的功能。weak.Pointer的主要作用是允许引用一个对象，而不会阻止该对象被垃圾收集器回收。具体来说，它允许你持有一个指向对象的指针，但当该对象的强指针消失时，垃圾收集器仍然可以回收该对象。下面是一张weak Pointer工作机制的示意图，展示了弱指针的生命周期以及对GC行为的影响：</p>
<p><img src="https://tonybai.com/wp-content/uploads/understand-go-unique-package-by-example-4.png" alt="" /></p>
<p>初始状态下，应用创建一个对象，同时创建一个强指针和一个weak.Pointer指向该对象。GC检查对象，但因为存在强指针，所以不能回收。强指针被移除，只剩下weak.Pointer指向对象。GC检查对象，发现没有强指针，于是回收对象。内存被释放，weak.Pointer变为nil。</p>
<p>由于weak包位于internal包中，它只能在Go的标准库或特定包中使用，我们只能用下面的伪代码来展示weak.Pointer的机制：</p>
<pre><code>package main

import (
    "fmt"
    "runtime"
    "unsafe"
    "internal/weak"
)

type MyStruct struct {
    name string
}

func main() {
    // 创建一个对象，obj可以理解为该对象的强指针
    obj := &amp;MyStruct{name: "object1"} 

    // 创建一个weak.Pointer指向obj，weakPtr是对obj指向内存的弱指针
    weakPtr := weak.Make(obj)

    // 显示对象的值，通过强指针和弱指针都可以
    fmt.Println("Before GC:", weakPtr.Value())
    fmt.Println("Before GC:", *obj)

    // 释放原始对象的强指针
    obj = nil

    // 强制执行GC，这时由于弱指针无法阻止GC，obj指向的内存可能被回收
    runtime.GC()

    // 查看弱指针是否仍然有效，这里不能直接使用obj，因为对象可能已经被回收
    fmt.Println("After GC:", weakPtr.Value())
}
</code></pre>
<p>弱指针有一些典型的使用场景，比如在缓存机制中，可能希望引用某些对象而不阻止它们被垃圾回收。这样可以在内存不足时自动释放不再使用的缓存对象；又比如在某些场景下，不希望对象长时间驻留在内存中，但仍然希望能够在需要时重新创建或加载它们，即延迟加载的对象；在某些数据结构中（如哈希表或链表），持有强指针可能会导致内存泄漏，弱指针可以有效避免这种情况。</p>
<blockquote>
<p>注：目前Knyszek已经提出proposal，<a href="https://github.com/golang/go/issues/67552">将weak包提升为标准库公共API</a>，该proposal已经被accept，最早将在Go 1.24版本落地。</p>
</blockquote>
<h3>3.3 unique包的优势</h3>
<p>从上面示例和原理示意图来看，unique包的设计和实现有几个显著的优势：</p>
<ul>
<li>泛型支持</li>
</ul>
<p>通过使用Go的泛型特性，unique包可以处理任何可比较的类型，大大扩展了其应用范围，不再局限于字符串类型。</p>
<ul>
<li>高效的内存管理</li>
</ul>
<p>unique包使用了运行时级别的弱指针实现，确保当所有相关的Handle&#91;T&#93;(即强指针)都不再被使用时，内部map中的值可以被垃圾回收，这既避免了内存长期占用，也避免了内存泄漏问题。</p>
<ul>
<li>快速比较操作</li>
</ul>
<p>Handle&#91;T&#93;类型的比较操作被优化为简单的指针比较，这比直接比较值(特别是对于大型结构体或长字符串内容)要快得多。</p>
<h3>3.4 unique包的实际应用</h3>
<p>unique包刚刚诞生，目前在Go标准库中的实际应用主要就是在net/netip包中，替代了之前由go4.org/intern移植到标准库中的internal/intern包。</p>
<p>net/netip包使用unique来优化Addr结构体中的addrDetail字段：</p>
<pre><code>type Addr struct {
    // 其他字段...

    // Details about the address, wrapped up together and canonicalized.
    z unique.Handle[addrDetail]
}

// addrDetail represents the details of an Addr, like address family and IPv6 zone.
type addrDetail struct {
    isV6   bool   // IPv4 is false, IPv6 is true.
    zoneV6 string // != "" only if IsV6 is true.
}

// z0, z4, and z6noz are sentinel Addr.z values.
// See the Addr type's field docs.
var (
    z0    unique.Handle[addrDetail]
    z4    = unique.Make(addrDetail{})
    z6noz = unique.Make(addrDetail{isV6: true})
)

// WithZone returns an IP that's the same as ip but with the provided
// zone. If zone is empty, the zone is removed. If ip is an IPv4
// address, WithZone is a no-op and returns ip unchanged.
func (ip Addr) WithZone(zone string) Addr {
    if !ip.Is6() {
        return ip
    }
    if zone == "" {
        ip.z = z6noz
        return ip
    }
    ip.z = unique.Make(addrDetail{isV6: true, zoneV6: zone})
    return ip
}
</code></pre>
<p>通过使用unique，net/netip包能够显著减少处理大量IP地址时的内存占用。特别是对于具有相同zone的IPv6地址，内存使用可以大幅降低。</p>
<p>下面我们也通过一个简单的示例来看看使用unique包的内存占用减少的效果。</p>
<h3>3.5 内存占用减少的效果</h3>
<p>现在我们创建100w个长字符串，这100w个字符串中，有1000种不同的字符串，相当于每种字符串有1000个重复值。下面分别用unique包和不用unique包来演示这个示例，看看内存占用情况：</p>
<pre><code>// unique-examples/effect_with_unique.go 

package main

import (
    "fmt"
    "runtime"
    "strings"
    "unique"
)

const (
    numItems    = 1000000
    stringLen   = 20
    numDistinct = 1000
)

func main() {
    // 创建一些不同的字符串
    distinctStrings := make([]string, numDistinct)
    for i := 0; i &lt; numDistinct; i++ {
        distinctStrings[i] = strings.Repeat(string(rune('A'+i%26)), stringLen)
    }

    // 使用unique包
    withUnique := make([]unique.Handle[string], numItems)
    for i := 0; i &lt; numItems; i++ {
        withUnique[i] = unique.Make(distinctStrings[i%numDistinct])
    }

    runtime.GC() // 强制GC
    printMemUsage("With unique")

    runtime.KeepAlive(withUnique)
}

func printMemUsage(label string) {
    var m runtime.MemStats
    runtime.ReadMemStats(&amp;m)
    fmt.Printf("%s:\n", label)
    fmt.Printf("  Alloc = %v MiB\n", bToMb(m.Alloc))
    fmt.Printf("  TotalAlloc = %v MiB\n", bToMb(m.TotalAlloc))
    fmt.Printf("  Sys = %v MiB\n", bToMb(m.Sys))
    fmt.Printf("  HeapAlloc = %v MiB\n", bToMb(m.HeapAlloc))
    fmt.Printf("  HeapSys = %v MiB\n", bToMb(m.HeapSys))
    fmt.Printf("  HeapInuse = %v MiB\n", bToMb(m.HeapInuse))
    fmt.Println()
}

func bToMb(b uint64) uint64 {
    return b / 1024 / 1024
}

// unique-examples/effect_without_unique.go
... 

func main() {
    // 创建一些不同的字符串
    distinctStrings := make([]string, numDistinct)
    for i := 0; i &lt; numDistinct; i++ {
        distinctStrings[i] = strings.Repeat(string(rune('A'+i%26)), stringLen)
    }

    // 不使用unique包
    withoutUnique := make([]string, numItems)
    for i := 0; i &lt; numItems; i++ {
        withoutUnique[i] = distinctStrings[i%numDistinct]
    }

    runtime.GC() // 强制GC以确保准确的内存使用统计
    printMemUsage("Without unique")

    runtime.KeepAlive(withoutUnique)
}

...
</code></pre>
<p>下面分别运行这两个源码：</p>
<pre><code>$go run effect_with_unique.go
With unique:
  Alloc = 7 MiB
  TotalAlloc = 7 MiB
  Sys = 15 MiB
  HeapAlloc = 7 MiB
  HeapSys = 11 MiB
  HeapInuse = 8 MiB

$go run effect_without_unique.go
Without unique:
  Alloc = 15 MiB
  TotalAlloc = 15 MiB
  Sys = 22 MiB
  HeapAlloc = 15 MiB
  HeapSys = 19 MiB
  HeapInuse = 15 MiB
</code></pre>
<p>这个结果清楚地显示了使用unique包后的内存节省。不使用unique包时，每个重复的字符串都会单独分配内存。而使用unique包后，相同的字符串只会分配一次，大大减少了内存使用。在实际应用中，内存节省的效果可能更加显著，特别是在处理大量重复数据（如日志处理、文本分析等）的场景中。</p>
<h2>4. 小结</h2>
<p>本文粗略探讨了Go 1.23版本引入的unique包：我们从字符串interning技术说起，介绍了Go社区在interning技术实现方面的努力历程，重点阐述了unique包的API设计、实现原理及其优势。</p>
<p>我们看到：unique包不仅支持传统的字符串interning，还扩展到任何可比较类型的值。其核心API设计简洁，通过Handle&#91;T&#93;类型和Make、Value方法实现了高效的值interning。</p>
<p>在实现原理上，unique包巧妙地结合了hashtrieMap、细粒度锁以及与runtime内gc相关函数，实现了支持可比较类型、interned值可被GC且支持快速比较的方案。</p>
<p>总的来说，unique包为Go开发者提供了一个强大而灵活的interning工具，有望在未来的Go社区项目中得到广泛应用。</p>
<p>本文涉及的源码可以在<a href="https://github.com/bigwhite/experiments/tree/master/unique-examples">这里</a>下载。</p>
<h2>5. 参考资料</h2>
<ul>
<li><a href="https://commaok.xyz/post/intern-strings/">Interning strings in Go</a> &#8211; https://commaok.xyz/post/intern-strings/</li>
<li><a href="https://en.wikipedia.org/wiki/String_interning">Interning</a> &#8211; https://en.wikipedia.org/wiki/String_interning</li>
<li><a href="https://github.com/golang/go/issues/62483">unique: new package with unique.Handle</a> &#8211; https://github.com/golang/go/issues/62483</li>
<li><a href="https://go.dev/blog/unique">New unique package</a> &#8211; https://go.dev/blog/unique</li>
<li><a href="https://github.com/golang/go/issues/69370">unique: large string still referenced, after interning only a small substring</a> &#8211; https://github.com/golang/go/issues/69370</li>
<li><a href="https://tailscale.com/blog/netaddr-new-ip-type-for-go?ref=tonybai.com">netaddr.IP: a new IP address type for Go</a> &#8211; https://tailscale.com/blog/netaddr-new-ip-type-for-go</li>
</ul>
<hr />
<p><a href="https://public.zsxq.com/groups/51284458844544">Gopher部落知识星球</a>在2024年将继续致力于打造一个高品质的Go语言学习和交流平台。我们将继续提供优质的Go技术文章首发和阅读体验。同时，我们也会加强代码质量和最佳实践的分享，包括如何编写简洁、可读、可测试的Go代码。此外，我们还会加强星友之间的交流和互动。欢迎大家踊跃提问，分享心得，讨论技术。我会在第一时间进行解答和交流。我衷心希望Gopher部落可以成为大家学习、进步、交流的港湾。让我相聚在Gopher部落，享受coding的快乐! 欢迎大家踊跃加入！</p>
<p><img src="http://image.tonybai.com/img/tonybai/gopher-tribe-zsxq-small-card.png" alt="img{512x368}" /><br />
<img src="http://image.tonybai.com/img/tonybai/go-programming-from-beginner-to-master-qr.png" alt="img{512x368}" /></p>
<p><img src="http://image.tonybai.com/img/tonybai/go-first-course-banner.png" alt="img{512x368}" /><br />
<img src="http://image.tonybai.com/img/tonybai/imooc-go-column-pgo-with-qr.jpg" alt="img{512x368}" /></p>
<p>著名云主机服务厂商DigitalOcean发布最新的主机计划，入门级Droplet配置升级为：1 core CPU、1G内存、25G高速SSD，价格5$/月。有使用DigitalOcean需求的朋友，可以打开这个<a href="https://m.do.co/c/bff6eed92687">链接地址</a>：https://m.do.co/c/bff6eed92687 开启你的DO主机之路。</p>
<p>Gopher Daily(Gopher每日新闻) &#8211; https://gopherdaily.tonybai.com</p>
<p>我的联系方式：</p>
<ul>
<li>微博(暂不可用)：https://weibo.com/bigwhite20xx</li>
<li>微博2：https://weibo.com/u/6484441286</li>
<li>博客：tonybai.com</li>
<li>github: https://github.com/bigwhite</li>
<li>Gopher Daily归档 &#8211; https://github.com/bigwhite/gopherdaily</li>
<li>Gopher Daily Feed订阅 &#8211; https://gopherdaily.tonybai.com/feed</li>
</ul>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。</p>
<p style='text-align:left'>&copy; 2024, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2024/09/18/understand-go-unique-package-by-example/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>GoCN社区Go读书会第二期：《Go语言精进之路》</title>
		<link>https://tonybai.com/2022/07/07/gocn-community-go-book-club-issue2-go-programming-from-beginner-to-master/</link>
		<comments>https://tonybai.com/2022/07/07/gocn-community-go-book-club-issue2-go-programming-from-beginner-to-master/#comments</comments>
		<pubDate>Thu, 07 Jul 2022 11:40:39 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[Amazon]]></category>
		<category><![CDATA[atomic]]></category>
		<category><![CDATA[benchmark]]></category>
		<category><![CDATA[B站]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[Cgo]]></category>
		<category><![CDATA[Channel]]></category>
		<category><![CDATA[crypto]]></category>
		<category><![CDATA[CSP]]></category>
		<category><![CDATA[defer]]></category>
		<category><![CDATA[delve]]></category>
		<category><![CDATA[error]]></category>
		<category><![CDATA[for]]></category>
		<category><![CDATA[Function]]></category>
		<category><![CDATA[functrace]]></category>
		<category><![CDATA[fuzzing]]></category>
		<category><![CDATA[GC]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[go-coding-in-go-way]]></category>
		<category><![CDATA[go-module]]></category>
		<category><![CDATA[go-test]]></category>
		<category><![CDATA[go.mod]]></category>
		<category><![CDATA[Go1]]></category>
		<category><![CDATA[go1.18]]></category>
		<category><![CDATA[GoCN]]></category>
		<category><![CDATA[GODEBUG]]></category>
		<category><![CDATA[GODEUG]]></category>
		<category><![CDATA[gofmt]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[Gopher]]></category>
		<category><![CDATA[gopherchina]]></category>
		<category><![CDATA[goroutine]]></category>
		<category><![CDATA[Go语言学习笔记]]></category>
		<category><![CDATA[Go语言精进之路]]></category>
		<category><![CDATA[Haskell]]></category>
		<category><![CDATA[http]]></category>
		<category><![CDATA[https]]></category>
		<category><![CDATA[if]]></category>
		<category><![CDATA[init]]></category>
		<category><![CDATA[Interface]]></category>
		<category><![CDATA[iota]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[leanpub]]></category>
		<category><![CDATA[Method]]></category>
		<category><![CDATA[metrics]]></category>
		<category><![CDATA[Module]]></category>
		<category><![CDATA[Package]]></category>
		<category><![CDATA[panic]]></category>
		<category><![CDATA[pitfalls]]></category>
		<category><![CDATA[pprof]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[receiver]]></category>
		<category><![CDATA[Reflect]]></category>
		<category><![CDATA[runtime]]></category>
		<category><![CDATA[RussCox]]></category>
		<category><![CDATA[select]]></category>
		<category><![CDATA[strings]]></category>
		<category><![CDATA[sync]]></category>
		<category><![CDATA[time]]></category>
		<category><![CDATA[TLS]]></category>
		<category><![CDATA[trap]]></category>
		<category><![CDATA[unsafe]]></category>
		<category><![CDATA[亚马逊]]></category>
		<category><![CDATA[代码风格]]></category>
		<category><![CDATA[作用域]]></category>
		<category><![CDATA[共享内存]]></category>
		<category><![CDATA[内存分配]]></category>
		<category><![CDATA[写书]]></category>
		<category><![CDATA[出书]]></category>
		<category><![CDATA[出版]]></category>
		<category><![CDATA[函数]]></category>
		<category><![CDATA[切片]]></category>
		<category><![CDATA[包导入]]></category>
		<category><![CDATA[华章书院]]></category>
		<category><![CDATA[原子操作]]></category>
		<category><![CDATA[反射]]></category>
		<category><![CDATA[变量声明]]></category>
		<category><![CDATA[坑]]></category>
		<category><![CDATA[垃圾回收]]></category>
		<category><![CDATA[复合字面值]]></category>
		<category><![CDATA[字符串]]></category>
		<category><![CDATA[导读]]></category>
		<category><![CDATA[常量]]></category>
		<category><![CDATA[并发]]></category>
		<category><![CDATA[并发编程]]></category>
		<category><![CDATA[接口]]></category>
		<category><![CDATA[文字稿]]></category>
		<category><![CDATA[方法]]></category>
		<category><![CDATA[机械工业出版社]]></category>
		<category><![CDATA[标准库]]></category>
		<category><![CDATA[汇编]]></category>
		<category><![CDATA[泛型]]></category>
		<category><![CDATA[泛读]]></category>
		<category><![CDATA[直播]]></category>
		<category><![CDATA[简单]]></category>
		<category><![CDATA[精读]]></category>
		<category><![CDATA[组合]]></category>
		<category><![CDATA[编程思维]]></category>
		<category><![CDATA[自助出版]]></category>
		<category><![CDATA[自媒体]]></category>
		<category><![CDATA[自带电池]]></category>
		<category><![CDATA[行动清单]]></category>
		<category><![CDATA[设计哲学]]></category>
		<category><![CDATA[语言]]></category>
		<category><![CDATA[运行时]]></category>
		<category><![CDATA[错误处理]]></category>
		<category><![CDATA[面向工程]]></category>
		<category><![CDATA[项目布局]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=3610</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2022/07/07/gocn-community-go-book-club-issue2-go-programming-from-beginner-to-master 本文是2022年6月26日我在GoCN社区的Go读书会第二期《Go语言精进之路》直播的文字稿。本文对直播的内容做了重新整理与修订，供喜欢阅读文字的朋友们在收看直播后的揣摩和参考。视频控的童鞋可以关注GoCN公众号和视频号看剪辑后的视频，也可以在B站GopherChina专区下收看视频回放(https://www.bilibili.com/video/BV1p94y1R7jg)。 大家晚上好，我叫白明，是《Go语言精进之路》一书的作者，也是tonybai.com的博主，很荣幸今天参加GoCN社区Go读书会第二期，分享一下我个人在写书和读书方面的经验和体会。 今天的分享包括三方面内容： 写书的历程。一些Gopher可能比较好奇，这么厚的一套书是怎么写出来的，今天就和大家聊一聊。 《Go语言精进之路》导读。主要是把这本书的整体构思与大家聊聊，希望通过这个导读帮助读者更好地阅读和理解这套书。 我个人的读书方法与经验的简要分享。 首先和大家分享一下写书的历程。 一. 写书的历程 1. 程序员的“小目标”与写书三要素 今天收看直播的童鞋都是有追求的技术人员，可能心底都有写一本属于自己的书的小目标。这样可以把自己学习到的知识、技能和经验以比较系统的方式输出给其他人，可以帮助其他人快速学习和掌握本领域的知识、技能和经验。 当然写书还有其他好处，比如：提升名气、更容易混技术圈子、可能给你带来更好的职业发展机会，当然也会给你带来一些额外的副业收入，至于多少，还要看书籍的口碑与销量。 那怎么才能写书呢？作为“过来人”，我总结了三个要素，也是三个条件。 第一个要素是能力。 这个很容易理解。以Go为例，如果你没有在Go语言方面的知识、技能的沉淀，没有对Go语言方方面面的较为深入的理解，你很难写出一本口碑很好的书籍。尤其是那种有原创性、独到见解的著书。而不是对前人资料做系统整理摘抄的编书。编书更常见于教材、字典等。显然著书对作者水平的要求更高。 第二个要素是意愿。 写过书的同学都有体会，写书是一件辛苦活。需要你在正式工作之余付出大量业余时间伏案创作。并且对于小众技术类书籍来说，写书能带来的金钱上的收益和你付出的时长和精力不成正比。就这个问题，我曾与机械工业出版社的营销编辑老师聊过，得到的信息是：Go技术书籍的市场与Java、Python还没法比，即便是像Go语言圣经《Go程序设计语言》的销量也没法与Java、Python的头部书籍销量相比。 第三个要素是机会。 记得小时候十分羡慕那些能出书的人，觉得都是大神级的人物。不过那个时候出书的确很难，机会应该很少，你要不是在学术圈里混很难出书。如今就容易地多了，渠道也多了。每年出版社都有自己的出版计划，各个出版社的编辑老师也在根据计划在各种自媒体上、技术圈子中寻觅匹配的技术作者。 如果你有自己的思路，也可以整理出大纲，并通过某种方式联系到出版社老师，如果匹配就可以出。 另外国外流行电子自助出版，这也给很多技术作者很好的出版机会。比如国内作者老貘写的Go 101系列就是在亚马逊和leanpub上做的自助出版，效果还不错。 以上就是我总结的出书的三个要素，一旦集齐这三个要素呢，出书实际就是自然而然的一件事了。以我为例。 从能力方面来说呢，我大约从2011年开始接触和学习Go语言，算是国内较早的一批Go语言接纳者。Go语言2012年才发布1.0版本，因此那时我接触的Go时还是r60版本，还不是正式的1.0版本。从那时起就一直在跟踪Go演化，日常写一些Go项目的小程序。 Go 1.5实现自举并大幅降低GC延迟，我于是开始在一些生产环境使用Go，并逐渐将知识和经验做了沉淀，在自己的博客上不断做着Go相关内容的输出，反响也不错。 随着输出Go内容的增多，我发现以博客的形式输出，内容组织零散，于是我第一次有了将自己的Go知识系统整理并输出的意愿和想法。 我在实践Go的过程中收到很多Go初学者的提问：Go入门容易，但精进难，怎么才能像Go开发团队那样写出符合Go思维和语言惯例的高质量代码呢？这个问题引发了我的思考。在2017年GopherChina大会我以《go coding in go way》为主题，以演讲的形式尝试回答这个问题，但鉴于演讲的时长有限，很多内容没能展开，效果不甚理想。这进一步增强了我通过书籍的形式系统解答这个问题的意愿。 而当时我家大宝已经长大了，我也希望通过写书这个行动身体力行地给孩子树立一个正面的榜样。中国古语有云：言传身教，我也想践行一下。 机会就这样自然而然的来了！2018年初，机械工业出版社副总编杨福川老师在微信联系到我，和我探讨一下是否可以写一本类似于“Effective Go”的书，当时机械工业出版社华章出版社策划了Effective XXX(编写高质量XXX)系列图书，当时已经出版了C、Python等语言版本的书籍，还差Go语言的。我的出书意愿与出版社的需求甚是匹配，于是我答应的杨老师的要求，成为了这套丛书的Go版本的作者。 2. 写书的过程 我是2018下旬开始真正动笔的。 真正开始码字的时候，我才意识到，写书真不容易，要写出高质量书稿，的确需付出大量时间和汗水。每天晚上、早上都在构思、码字、写代码示例、画插图，睡眠时间很少。记得当时每周末都在奋笔疾书，陪伴家人尤其是孩子的时间很少。 另外我这个人还习惯于把一个知识点讲细讲透，这样每一节的篇幅都不小。因此，写作进展是很缓慢的，就这样，进度一再延期。好在编辑老师比较nice，考虑到书稿质量，没有狠狠催进度。 2020年11月末，我正式向出版社交了初稿，记得初稿有66条，近40w字。 又经过一年的排期、编辑、修订、排版，2021年12月下旬正式出版。 2022年1月《Go语言精进之路》正式上架到各个渠道货架。 到今天为止，出版了近六个月，这本书收获了还不错的口碑，在各个平台上的口碑都在8分以上(注：口碑分数还在动态变化，下图仅为当时的快照，不代表如今的分数)。 能获得大家的认可，让我很是欣慰，觉得写书过程付出的辛苦没有白费。 以上就是我的写书历程。总的来说一句话：写书不易，写高质量的书更难。 接下来我来进行一下《Go语言精进之路》一书的导读。 二. 《Go语言精进之路》导读 也许是“用力过猛”，《Go语言精进之路》一书写的太厚了，无法装订为一册。编辑老师建议装订为两册，即1、2册。很多同学好奇为什么不是上下册而是1、2册，这里是编辑老师的“高瞻远瞩”，目的是为后续可能的“续写”(比如第3册)留足空间，毕竟Go语言还在快速演进，目前的版本还不包含像泛型这样的新语法。不过，目前第3册还尚未列入计划。 [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/gocn-community-go-book-club-issue2-go-programming-from-beginner-to-master-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2022/07/07/gocn-community-go-book-club-issue2-go-programming-from-beginner-to-master">本文永久链接</a> &#8211; https://tonybai.com/2022/07/07/gocn-community-go-book-club-issue2-go-programming-from-beginner-to-master</p>
<p>本文是2022年6月26日我在<strong>GoCN社区的Go读书会第二期《Go语言精进之路》直播的文字稿</strong>。本文对直播的内容做了重新整理与修订，供喜欢阅读文字的朋友们在收看直播后的揣摩和参考。视频控的童鞋可以关注<strong>GoCN公众号和视频号</strong>看剪辑后的视频，也可以<a href="https://www.bilibili.com/video/BV1p94y1R7jg">在B站GopherChina专区下收看视频回放</a>(https://www.bilibili.com/video/BV1p94y1R7jg)。</p>
<hr />
<p>大家晚上好，我叫白明，是<a href="https://item.jd.com/13694000.html">《Go语言精进之路》</a>一书的作者，也是<a href="https://tonybai.com">tonybai.com</a>的博主，很荣幸今天参加GoCN社区Go读书会第二期，分享一下我个人在写书和读书方面的经验和体会。</p>
<p>今天的分享包括三方面内容：</p>
<ul>
<li>写书的历程。一些Gopher可能比较好奇，这么厚的一套书是怎么写出来的，今天就和大家聊一聊。</li>
<li>《Go语言精进之路》导读。主要是把这本书的整体构思与大家聊聊，希望通过这个导读帮助读者更好地阅读和理解这套书。</li>
<li>我个人的读书方法与经验的简要分享。</li>
</ul>
<p>首先和大家分享一下写书的历程。</p>
<h2>一. 写书的历程</h2>
<p><img src="https://tonybai.com/wp-content/uploads/gocn-community-go-book-club-issue2-go-programming-from-beginner-to-master-2.png" alt="" /></p>
<h3>1. 程序员的“小目标”与写书三要素</h3>
<p>今天收看直播的童鞋都是有追求的技术人员，可能心底都有写一本属于自己的书的小目标。这样可以把自己学习到的知识、技能和经验以比较系统的方式输出给其他人，可以帮助其他人快速学习和掌握本领域的知识、技能和经验。</p>
<p>当然写书还有其他好处，比如：提升名气、更容易混技术圈子、可能给你带来更好的职业发展机会，当然也会给你带来一些额外的副业收入，至于多少，还要看书籍的口碑与销量。</p>
<p>那怎么才能写书呢？作为“过来人”，我总结了三个要素，也是三个条件。</p>
<p>第一个要素是<strong>能力</strong>。</p>
<p>这个很容易理解。以Go为例，如果你没有在Go语言方面的知识、技能的沉淀，没有对Go语言方方面面的较为深入的理解，你很难写出一本口碑很好的书籍。尤其是那种有原创性、独到见解的著书。而不是对前人资料做系统整理摘抄的编书。编书更常见于教材、字典等。显然著书对作者水平的要求更高。</p>
<p>第二个要素是<strong>意愿</strong>。</p>
<p>写过书的同学都有体会，写书是一件辛苦活。需要你在正式工作之余付出大量业余时间伏案创作。并且对于小众技术类书籍来说，写书能带来的金钱上的收益和你付出的时长和精力不成正比。就这个问题，我曾与机械工业出版社的营销编辑老师聊过，得到的信息是：Go技术书籍的市场与Java、Python还没法比，即便是像Go语言圣经《Go程序设计语言》的销量也没法与Java、Python的头部书籍销量相比。</p>
<p>第三个要素是<strong>机会</strong>。</p>
<p>记得小时候十分羡慕那些能出书的人，觉得都是大神级的人物。不过那个时候出书的确很难，机会应该很少，你要不是在学术圈里混很难出书。如今就容易地多了，渠道也多了。每年出版社都有自己的出版计划，各个出版社的编辑老师也在根据计划在各种自媒体上、技术圈子中寻觅匹配的技术作者。</p>
<p>如果你有自己的思路，也可以整理出大纲，并通过某种方式联系到出版社老师，如果匹配就可以出。</p>
<p>另外国外流行电子自助出版，这也给很多技术作者很好的出版机会。比如国内作者老貘写的<a href="https://go101.org/">Go 101系列</a>就是在<a href="https://kdp.amazon.com/en_US/">亚马逊</a>和<a href="https://leanpub.com">leanpub</a>上做的自助出版，效果还不错。</p>
<p>以上就是我总结的出书的三个要素，一旦集齐这三个要素呢，出书实际就是自然而然的一件事了。以我为例。</p>
<p>从能力方面来说呢，我大约从2011年开始接触和学习Go语言，算是国内较早的一批Go语言接纳者。Go语言2012年才发布1.0版本，因此那时我接触的Go时还是r60版本，还不是正式的1.0版本。从那时起就一直在跟踪Go演化，日常写一些Go项目的小程序。</p>
<p>Go 1.5实现自举并大幅降低GC延迟，我于是开始在一些生产环境使用Go，并逐渐将知识和经验做了沉淀，在自己的博客上不断做着Go相关内容的输出，反响也不错。</p>
<p>随着输出Go内容的增多，我发现以博客的形式输出，内容组织零散，于是我第一次有了将自己的Go知识系统整理并输出的意愿和想法。</p>
<p>我在实践Go的过程中收到很多Go初学者的提问：Go入门容易，但精进难，怎么才能像Go开发团队那样写出符合Go思维和语言惯例的高质量代码呢？这个问题引发了我的思考。在2017年GopherChina大会我以<a href="https://tonybai.com/2017/04/20/go-coding-in-go-way/">《go coding in go way》</a>为主题，以演讲的形式尝试回答这个问题，但鉴于演讲的时长有限，很多内容没能展开，效果不甚理想。这进一步增强了我通过书籍的形式系统解答这个问题的意愿。</p>
<p>而当时我家<a href="https://daughter.tonybai.com">大宝</a>已经长大了，我也希望通过写书这个行动身体力行地<strong>给孩子树立一个正面的榜样</strong>。中国古语有云：<strong>言传身教</strong>，我也想践行一下。</p>
<p>机会就这样自然而然的来了！2018年初，机械工业出版社副总编杨福川老师在微信联系到我，和我探讨一下是否可以写一本类似于“Effective Go”的书，当时机械工业出版社华章出版社策划了Effective XXX(编写高质量XXX)系列图书，当时已经出版了C、Python等语言版本的书籍，还差Go语言的。我的出书意愿与出版社的需求甚是匹配，于是我答应的杨老师的要求，成为了这套丛书的Go版本的作者。</p>
<h3>2. 写书的过程</h3>
<p>我是2018下旬开始真正动笔的。</p>
<p>真正开始码字的时候，我才意识到，写书真不容易，要写出高质量书稿，的确需付出大量时间和汗水。每天晚上、早上都在构思、码字、写代码示例、画插图，睡眠时间很少。记得当时每周末都在奋笔疾书，陪伴家人尤其是孩子的时间很少。</p>
<p>另外我这个人还习惯于把一个知识点讲细讲透，这样每一节的篇幅都不小。因此，写作进展是很缓慢的，就这样，进度一再延期。好在编辑老师比较nice，考虑到书稿质量，没有狠狠催进度。</p>
<p>2020年11月末，我正式向出版社交了初稿，记得初稿有66条，近40w字。</p>
<p>又经过一年的排期、编辑、修订、排版，2021年12月下旬正式出版。</p>
<p>2022年1月《Go语言精进之路》正式上架到各个渠道货架。</p>
<p>到今天为止，出版了近六个月，这本书收获了还不错的口碑，在各个平台上的口碑都在8分以上(注：口碑分数还在动态变化，下图仅为当时的快照，不代表如今的分数)。</p>
<p><img src="https://tonybai.com/wp-content/uploads/gocn-community-go-book-club-issue2-go-programming-from-beginner-to-master-5.jpg" alt="" /><br />
<img src="https://tonybai.com/wp-content/uploads/gocn-community-go-book-club-issue2-go-programming-from-beginner-to-master-6.png" alt="" /></p>
<p>能获得大家的认可，让我很是欣慰，觉得写书过程付出的辛苦没有白费。</p>
<p>以上就是我的写书历程。总的来说一句话：<strong>写书不易，写高质量的书更难</strong>。</p>
<p>接下来我来进行一下《Go语言精进之路》一书的导读。</p>
<h2>二. 《Go语言精进之路》导读</h2>
<p><img src="https://tonybai.com/wp-content/uploads/gocn-community-go-book-club-issue2-go-programming-from-beginner-to-master-3.png" alt="" /></p>
<p>也许是“用力过猛”，《Go语言精进之路》一书写的太厚了，无法装订为一册。编辑老师建议装订为两册，即1、2册。很多同学好奇为什么不是上下册而是1、2册，这里是编辑老师的“高瞻远瞩”，目的是为后续可能的“续写”(比如第3册)留足空间，毕竟Go语言还在快速演进，目前的版本还不包含像<a href="https://tonybai.com/2022/04/20/some-changes-in-go-1-18">泛型这样的新语法</a>。不过，目前第3册还尚未列入计划。</p>
<p>本套书共分为10个部分，66个主题。第一册包含了前7个部分，后3部分在第二册中。</p>
<h3>1. 整体写作思路</h3>
<p>整套书围绕着两个前后关联的思路循序展开。</p>
<p>第一个思路我叫它：<strong>精进之路，思维先行</strong>。</p>
<p>第二个思路称为：<strong>践行哲学，遵循惯例，认清本质，理解原理</strong>。</p>
<p>我们先来看看第一个思路。</p>
<h3>2. 精进之路，思维先行</h3>
<p>收看直播的童鞋都不止学过一门编程语言。大家可能都有过这样的经历：你已经精通A语言，然后在学习B语言的时候用A语言的思维去写B代码，你会觉得写出的B代码很别扭，写出的代码总是感觉不是很地道，总觉得不是那种高质量的B语言代码。</p>
<p>其实，不仅学习编程语言是这样，学自然语言也是一样。最典型的一个例子，大家都学过十几年的英语，但毕业后能用地道的英语表达自己观点的人却不多，为什么呢？那就是我们总用中文的思维方式去组织英语的句子，去说英语，这样再怎么努力也很难上一个层次。</p>
<p>其实，很多语言大师早就意识到了这一点。下面是我收集的这些大师的关于语言与思维的论点，这里和大家分享一下：</p>
<blockquote>
<p>“语言决定思维方式” &#8211; 萨丕尔假说</p>
<p>“我的语言之局限，即我的世界之局限” &#8211;  路德维希·维特根斯坦，语言哲学的奠基人</p>
<p>“不能改变你思维方式的语言，不值得学习” &#8211; Alan Perlis（首届ACM图灵奖得主)</p>
</blockquote>
<p>我们看到：无论是自然语言界的大师，还是IT界的大佬，他们的观点异曲同工。总之一句话：<strong>语言要精进，思维要先行</strong>。</p>
<h3>3. Part1：进入Go语言编程思维导引</h3>
<p>正是因为意识到语言与思维的紧密关系，我在书的第一部分就安排了Go语言编程思维导引，希望大家意识到Go编程思维在语言精进之路上的重要性。</p>
<p>一门编程语言的思维也不是与生俱来的，而是在演进中逐步形成的。所以在这一部分，我安排了Go诞生与演进、Go设计哲学：简单、组合、并发、面向工程。这样做的目的是让大家一起了解Go语言设计者在设计Go语言时的所思所想，让读者站在语言设计者的高度理解Go语言与众不同的设计，认同Go语言的设计理念。因为这些是<strong>Go编程语言思维形成的“土壤”</strong>。</p>
<p>这一部分最后一节是Go编程思维举例导引，书中给出了C, Haskell和Go程序员在面对同一个问题时，首先考虑到的思维方式以及不同思维下代码设计方式的差异。</p>
<p>知道Go编程思维的重要性后，我们应该怎么做呢？</p>
<h3>4. 怎么学习Go编程思维？</h3>
<p>学习的本质是一种模仿。<strong>要学习Go思维，就要去模仿Go团队、Go社区的优秀项目和代码，看看他们怎么做的</strong>。这套书后面的部分讲的就是这个。而“践行哲学，遵循惯例，认清本质，理解原理”就是对后面内容的写作思路的概要性总结。</p>
<ul>
<li>践行哲学</li>
</ul>
<p>把Go设计哲学用于自己的项目的设计实践中，而不是仅停留在口头知道上。</p>
<ul>
<li>遵循惯例</li>
</ul>
<p>遵循Go团队的一些语言惯例，比如“comma，ok”、使用复合字面值初始化等，使用这些惯例你可以让你的代码显得很地道，别人一看就懂。</p>
<ul>
<li>认清本质</li>
</ul>
<p>为了更高效地利用语言机制，我们要认清一些语言机制背后的本质，比如切片、字符串在运行时的表示，这样一来既能帮助开发人员正确使用这些语法元素，同时也能避免入坑。</p>
<ul>
<li>理解原理</li>
</ul>
<p>Go带有运行时。运行时全程参与Go应用生命周期，因此，只有对Goroutine调度、GC等原理做适当了解，才能更好的发挥Go的威力。</p>
<p>这套书的part2-part10 就是基于对Go团队、Go社区优秀实践与惯例的梳理，用系统化的思路构建出来并循序渐进呈现给大家的。</p>
<h3>5. Part2 – 项目基础：布局、代码风格与命名</h3>
<p>这部门的内容是每个gopher在开启一个Go项目时都要考虑的事情。</p>
<ul>
<li>项目布局</li>
</ul>
<p>我见过很多Gopher问项目布局的事情，因为Go官方没有给出标准布局。本书讲解了Go项目的结构布局的演进历程以及Go社区的事实标准，希望能给大家提供足够的参考信息。</p>
<ul>
<li>代码风格</li>
</ul>
<p>针对Go代码风格，由于代码风格在Go中已经弱化，所以这里主要还是带大家理解gofmt存在的意义和使用方法。</p>
<ul>
<li>命名惯例</li>
</ul>
<p>关于命名，我不知道大家是否觉得命名难，但对我来说是挺难的，我总是绞尽脑汁在想用啥名(手动允悲)。所以我的原则是“代码未动，命名先行”。 对于Go中变量、标识符等的命名惯例这样的“关键的问题”，我使用了“笨方法”：我统计了Go标准库、Docker库、k8s库的命名情况，并分门别类给出不同语法元素的命名惯例，具体内容大家可以看书了解 。</p>
<h3>6. Part3 – 语法基础：声明、类型、语句与控制结构</h3>
<p>第三部分讲的很基础，但内容还是要高于基础的。</p>
<ul>
<li>一致的变量声明</li>
</ul>
<p>我们知道Go提供多种变量声明方式，但是在不同位置该用哪种声明方式可读性好又不容易造坑呢(尤其要注意短变量声明)？书中给出了系统阐述。</p>
<ul>
<li>无类型常量与iota</li>
</ul>
<p>大家都用过常量，但很多人对于无类型常量与有类型常量区别不了解，书中帮你做了总结。还有，很多人用过iota，但却不理解iota的真正含义以及它能帮你做啥。书中对iota的语义做了说明，对常见用途做了梳理。</p>
<ul>
<li>零值可用</li>
</ul>
<p>Go提倡零值可用，也内置了有很多零值可用类型，用起来很爽，比如：切片(不全是，仅在append时是零值可用，当用下标访问时，不具备零值可用)、sync包中的Mutex、RDMutex等</p>
<p>其实类比于线程（thread），goroutine也是一种零值可用的“类型”，只是Go没有goroutine这个类型罢了。</p>
<p>如果我们是包的设计者，如果提供零值可用的类型，可以提升包的使用者的体验。</p>
<ul>
<li>复合字面值来初始化</li>
</ul>
<p>使用复合字面值对相应的变量进行初始化是一个Go语言的惯例， Go虽然提供了new和make，但日常很少用，尤其是new。</p>
<ul>
<li>切片、字符串、map的原理、惯用法与坑</li>
</ul>
<p>Go是带有runtime的语言，语法层面展示的很多语法元素和runtime层真实的表示并不一致。要想高效利用这些类型，如果不了解runtime层表示还真不行。有时候还有很严重的“坑”。懂了，自然就能绕过坑。</p>
<ul>
<li>包导入</li>
</ul>
<p>Go源文件的import语句后面跟着的是包名还是包路径？Go编译是不是必须要有依赖项的源码才可以，只有.a是否可以？这些问题书中都有系统说明</p>
<ul>
<li>代码块与作用域</li>
</ul>
<p>代码块与作用域是Go语言的基础概念，虽然基础，如果理解不好，也是有“坑”的，比如最常见的变量遮蔽等。一旦理解透了，还可以帮你解决意想不到的语法问题和执行语义错误问题。</p>
<ul>
<li>控制语句</li>
</ul>
<p>Go倡导“一个问题只有一种解决方法”。Go针对每种控制语句仅提供一种语法形式。虽然仅有一种形式，用不好，一样容器掉坑。本套书总结了Go控制语句的惯用法与使用注意事项。</p>
<h3>7. Part4 – 语法基础：函数与方法</h3>
<p>我们日常编写的Go代码逻辑都在函数或方法中，函数/方法是Go程序逻辑的基本承载单元。</p>
<ul>
<li>init函数</li>
</ul>
<p>init函数是包初始化过程中执行的函数，它有很多特殊用途。并且其初始化顺序对程序执行语义也有影响，这方面要搞清楚。书中对init函数的常见用途做了梳理，比如database/sql包的驱动自注册模式等。</p>
<ul>
<li>成为“一等公民” </li>
</ul>
<p>在Go中，函数成为了“一等公民”。函数成为一等公民后可以像变量一样，被作为参数传递到函数中、作为返回值从函数中返回、作为右值赋值给其他变量等，书中系统讲解了这个特性都有哪些性质和特殊应用，比如函数式编程等。</p>
<ul>
<li>defer语句的惯用法与坑</li>
</ul>
<p>defer就是帮你简化代码逻辑的，书中总结了defer语句的应用模式。以及使用defer的注意事项，比如函数求值时机、使用开销等。</p>
<ul>
<li>变长参数函数</li>
</ul>
<p>Go支持变长参数函数。大家可以没有意识到：变长参数函数是我们日常用的最多的一类函数，比如append函数、fmt.Printf系列、log包中提供的按日志严重级别输出日志的函数等。</p>
<p>但变长参数函数可能也是我们自己设计与实现较少的一类函数形式。 变长参数函数能帮我们做什么呢？书中讲解了变长参数函数的常见用途，比如实现功能选项模式等。</p>
<ul>
<li>方法的本质、receiver参数类型选择、方法集合</li>
</ul>
<p>方法的本质其实是函数，弄清楚方法的本质可以帮助我们解决很多难题，书中以实例方式帮助大家理解这一点。</p>
<p>方法receiver参数类型的选择也是Go初学者的常见困惑，这里书中给出三个原则，参照这三个原则，receiver类型选择就不是问题了。</p>
<p>怎么确定一个类型是否实现接口？我们需要看类型的方法集合。那么确定一个类型方法集合就十分重要，尤其是那些包括类型嵌入的类型的方法集合，书中对这块内容做了系统的讲解。</p>
<h3>8. Part5 – 语法核心：接口</h3>
<ul>
<li>接口的内部表示</li>
</ul>
<p>接口是Go语言中的重要语法。Russ Cox曾说过：“如果要从Go语言中挑选出一个特性放入其他语言，我会选择接口”。可见接口的重要性。不过，用好接口类型的前提是理解接口在runtime层的表示，这一节会详细说明空接口与非空接口的内部表示。</p>
<ul>
<li>接口的设计惯例</li>
</ul>
<p>我们应该设计什么样的接口呢？ 大接口有何弊端？小接口有何优势？多小的接口算是合理的呢？这些在本节都有说明。</p>
<ul>
<li>接口与组合</li>
</ul>
<p>组合是Go的设计哲学，Go是关于组合的语言。接口在面向组合编程时将发挥重要作用。这里我将提到Go的两种组合方式：垂直组合和水平组合。其中接口类型在水平组合中起到的关键性的作用。书中还讲解了通过接口进行水平组合的几种模式：包裹模式、适配器函数、中间件等。</p>
<p>很多初学者告诉我，他们做了一段时间Go编码了，但还没有自己设计过接口，我建议这样的同学好好读读这一部分。</p>
<h3>9. Part6 – 语法核心：并发编程</h3>
<ul>
<li>并发设计vs并行设计</li>
</ul>
<p>学习并发编程首先要搞懂并发与并行的概念，书中用了一个很形象的机场安检的例子，来告诉大家并发与并行的区别。并发关乎结构，并行关注执行</p>
<ul>
<li>并发原语的原理与应用模式</li>
</ul>
<p>Go实现了csp模型，提供了goroutine、channel、select并发原语。</p>
<p>理解go并发编程。首先要深入理解基于goroutine的并发模型与调度方式。书中对这方面做了深入浅出的讲解，不涉及太多代码，相信大家都能看懂。</p>
<p>书中还对比了go并发模型，一种是csp，一种是传统的基于共享内存方式，并列举了Go并发的常见模式，比如创建、取消、超时、管道模式等。</p>
<p>另外，channel作为goroutine间通信的标准原语，有很多玩法，这里列举了常见的模式和使用注意事项。</p>
<ul>
<li>低级同步原语(sync和atomic)</li>
</ul>
<p>虽然有了CSP模型的并发原语，极大简化并发编程，但是sync包和原子操作也不能忘记，很多性能敏感的临界区还需要sync包/atomic这样的低级同步原语来同步。</p>
<h3>10. Part7 – 错误处理</h3>
<p>单独将错误处理拎出来，是因为很多人尤其是来自java的童鞋，习惯了try-catch-finally的结构化错误处理，看到go的错误处理就让其头疼。</p>
<p>Go语言十分重视错误处理，但它也的确有着相对保守的设计和显式处理错误的惯例。</p>
<p>本部分涵盖常见Go错误处理的策略、避免if err != nil写太多的方案，更为重要的是panic与错误处理的差别。我见过太多将panic用作正常处理的同学了。尤其是来自java阵营的童鞋。</p>
<h3>11. Part8 – 编程实践：测试、调试与性能剖析</h3>
<p>本部分聚焦编码之外的Go工具链工程实践。</p>
<ul>
<li>Go测试惯例与组织形式 </li>
</ul>
<p>这部分首先和大家聊聊go test包的组织形式，包括是选择包内测试还是包外测试？何时采用符合go惯例的表驱动的测试用例组织形式？如何管理测试依赖的外部数据文件等。</p>
<ul>
<li>模糊测试(fuzzing test)。</li>
</ul>
<p>这里的模糊测试并非基于go 1.18的原生<a href="https://tonybai.com/2021/12/01/first-class-fuzzing-in-go-1-18">fuzzing test</a>进行，写书的时候go 1.18版本尚未发布，而是基于德米特里-维尤科夫的<a href="http://tonybai.com/2015/12/08/go-fuzz-intro/">go-fuzz工具</a>。</p>
<ul>
<li>性能基准测试、度量数据与pprof性能剖析</li>
</ul>
<p>Go原生提供性能基准测试。这一节讲解了如何做性能基准测试、如何编写串行与并行的测试、性能基准测试结果比较工具以及如何排除额外干扰，让结果更准确等方面内容。在讲解pprof性能剖析工具时，我使用一个实例进行剖析讲解，这样理解起来更为直观。</p>
<ul>
<li>Go调试</li>
</ul>
<p>说到Go调试，我们日常使用最多的估计还是print大法。但在print大法之外，其实有一个事实标准的Go调试工具，它就是delve。在这一节中，我讲解了delve的工作原理以及使用delve如何实现并发调试、coredump调试以及在线挂接(attach)进程的调试。</p>
<h3>12. Part9 – 标准库、反射与cgo</h3>
<p>go是自带电池，开箱即用的语言，拥有高质量的标准库。在国外有些Gopher甚至倡导仅依赖标准库实现go应用。</p>
<ul>
<li>高频使用的标准库包（net、http、strings、time、crypto等)</li>
</ul>
<p>在这一节，我对高频使用的标准库包的原理和使用进行拆解分析，net、http、标准库io模型、strings、time、crypto等以帮助大家更高效的运用标准库。</p>
<ul>
<li>reflect包使用的三大法则</li>
</ul>
<p>reflect包为go提供了反射能力，书中对反射的实现原理做了讲解，重点是reflect使用的三大法则。</p>
<ul>
<li>cgo使用</li>
</ul>
<p>cgo不是go，但是cgo机制是使用go与c交互的唯一手段。书中对cgo的用法与约束做了详细讲解，尤其是在cgo开启的情况下如何做静态编译值得大家细读。</p>
<ul>
<li>unsafe包的安全使用法则</li>
</ul>
<p>事实证明unsafe包很有用，但要做到安全使用unsafe包，尤其是unsafe.Pointer，需要遵循一定的安全使用法则。书中对此做了举例详细说明。</p>
<p>反射、cgo、unsafe算是高级话题，要透彻理解，需要多阅读几遍书中内容并结合实践。</p>
<h3>13. Part10 – 工程实践</h3>
<ul>
<li>go module</li>
</ul>
<p>go module在<a href="https://tonybai.com/2018/11/19/some-changes-in-go-1-11/">go 1.11版本</a>中引入go，在<a href="https://tonybai.com/2021/02/25/some-changes-in-go-1-16">go 1.16版本</a>中成为go官方默认构建模式。go程序员入门go，精进go都跨不过go module这道坎儿。书中对go module构建模式做了超级系统的讲解：从go构建模式演进历史、go module的概念、原理、惯例、升降级major版本的操作，到使用注意事项等。不过这里还有有一些瑕疵，那就是go module这一节放置的位置太靠后了，应该往往前面提提。如果后面有修订版，可以考虑这么做。</p>
<ul>
<li>自定义go包导入路径</li>
</ul>
<p>书中还给出了一个自定义go包导入路径的一种实现方案，十分适合组织内部的私有仓库，有兴趣的同学可以重点看看。</p>
<ul>
<li>go命令的使用模式详解</li>
</ul>
<p>这一节将go命令分门别类地进行详细说明。包括：</p>
<pre><code>- 获取与安装的go get/go install
- go包检视的go list
- go包构建的go build
- 运行与诊断的GODEBUG、GOGC等环境变量的功用
- 代码静态检查与重构
- 文档查看
- go代码生成go generate
</code></pre>
<ul>
<li>Go常见的“坑”</li>
</ul>
<p>这一节将Go常见的“坑”进行了一次检阅。我这里将坑分为“语法类”和“标准库类”，并借鉴了央视五套天下足球top10节目，对每个坑的“遇坑指数”与“坑害指数”做了点评。</p>
<h3>14. 具备完整的示例代码与勘误表</h3>
<p>这套书拥有具备完整的示例代码与<a href="https://github.com/bigwhite/GoProgrammingFromBeginnerToMaster">勘误表</a>，它们都被持续维护，让大家没有读书的后顾之忧。</p>
<h2>三. 读书的实践与体会</h2>
<p><img src="https://tonybai.com/wp-content/uploads/gocn-community-go-book-club-issue2-go-programming-from-beginner-to-master-4.png" alt="" /></p>
<p>下面我再分享一下我个人是怎么读书的，包括go技术书籍的读书历程，以及关于读书的一些实践体会。</p>
<p>读书是千人千面的事，没有固定标准的。我的读书方法也不见得适合诸位。大家听听即可，觉得还不错，能借鉴上就最好了。</p>
<p>今天收看直播估计以gopher为主，所以首先说说Go语言书籍的阅读历程</p>
<h3>1. Go语言书籍阅读历程：先外后内</h3>
<p>对于IT技术类图书，初期还是要看原版的。这个没办法，因为it编程技术绝大多数来自国外。</p>
<p>我读的第一本Go技术书就是《the way to go》，至今这本书也没有引入国内。这是一本Go语言百科全书，大多数内容如今仍适用。唯一不足是该书成书于Go 1.0发布之前，使用的好像是r60版本，有少部分内容已经不适用。</p>
<p>后来Go 1.0发布后，我还陆续读过Addison-Wesley出版的《programming in go》和《The Go Programming Language Phrasebook》，两本书都还不错。</p>
<p>2015年末的布莱恩.克尼根和go核心团队的多诺万联合编写的《The Go Programming Language》，国内称之为Go圣经的书出版了，这让外文go技术书籍达到了巅峰，后来虽然也有go书籍书籍陆续出版，但都无法触及go圣经的地位。</p>
<p>说完外文图书，我再来说说中文Go图书的阅读历程。</p>
<p>我读过的第一本中文Go书籍是2012年许式伟老师的《Go语言编程》，很佩服许老师的眼光和魄力，七牛云很早就在生产用go。</p>
<p>第二本中文Go书籍是雨痕老师的《go学习笔记》，这也是国内第一本深入到go底层原理的书籍(后半部分)，遗憾的是书籍停留在go 1.5(还是go 1.6)的实现上，没有随Go版本演进而持续更新。</p>
<p>柴大和曹大合著的《go高级编程》也是一本不错的go技术书籍，如果你要深入学习cgo和go汇编，建议阅读此书。</p>
<p>后面的《Go语言底层原理剖析》和《Go语言设计与实现》也都是以深入了解Go运行机制为目标的书籍，口碑都很好，对这方面内容感兴趣的gopher，可以任意挑一本学习。</p>
<h3>2. 自己的读书方法</h3>
<p>我的读书方法其实不复杂，主要分为精读和泛读。</p>
<ul>
<li>阅读方式：好书精读，闲书泛读</li>
</ul>
<p>好书，集中一大段时间内进行阅读。 闲书(不烧脑)，通常是 碎片化阅读。</p>
<ul>
<li>精读方法：摘录+脑图+行动清单</li>
</ul>
<p>摘录就是将书中的观点和细节摘录出来，放到读书笔记，最好能用自己的语言重新描述出来，这样印象深刻，理解更为透彻。</p>
<p>脑图，概括书的思维脉络，防止读完就忘记。 通过脑图，我至少看着脉络能想起来。</p>
<p>行动清单：如果没有能输出行动清单，那这本书对你来说意义就不大。 什么是好书，好书就是那种看完后很迫切的想基于书中的观点做点什么。行动清单将有助于我在后续的行动中反复理解书中内容，提高知识的消化率和理解深度。</p>
<ul>
<li>泛读方法：碎片化+听书</li>
</ul>
<p>泛读主要是碎片化快读或听书，主要是坐地铁，坐公交，散步时。开车时在保证安全的前提下，可以用听书的方式。</p>
<h2>四. 小结</h2>
<p>本次分享了三块内容，这里小结一下：</p>
<ul>
<li>写书历程和写书三要素：能力 + 意愿 + 机会；</li>
<li>Go精进之路导读：思维先行，践行哲学，遵循惯例，认清本质，理解原理；</li>
<li>读书方法：选高质量图书精读(脑图+细节摘录+行动清单）。</li>
</ul>
<h2>五. Q&amp;A</h2>
<ul>
<li>在实际开发中有没有什么优雅的处理error的方法？</li>
</ul>
<p>建议看《Go语言精进之路》第一册第七部分中关于error处理的内容。</p>
<ul>
<li>是否在工作中使用过六边形架构以及依赖注入的处理经验?</li>
</ul>
<p>暂没有使用过六边形架构，生产中没有使用过Go第三方依赖注入的方案。</p>
<ul>
<li>后面会有泛型和模糊测试的补充么？</li>
</ul>
<p>从书籍内容覆盖全面性的角度而言，我个人有补充上述内容的想法，但还要看现在这套书的销售情况以及出版社的计划。目前还没列入个人工作计划。</p>
<ul>
<li>作者总结一系列go方法论、惯例等很实用，这种有逻辑的思考和见解是怎么形成的？</li>
</ul>
<p>没有特意考虑过是怎么形成的。个人平时喜欢多问自己几个为什么，形成让自己信服的工作和学习逻辑。(文字稿补充：同理心、多总结、多复盘、多输出)。</p>
<p>学习Go惯例、方法论，可以多多看Go语言开源项目自身的代码评审，看看Go contributor写代码的思路和如何评审其他贡献者的代码的。(文字稿补充：在这一过程中，潜移默化的感受Go编程思维)。</p>
<ul>
<li>如何阅读大型go项目的源码？</li>
</ul>
<p>我个人的方法就是自上而下。先拆分结构，然后找入口。如果是一个可执行的go程序，还是从入口层层的向后看。然后通过一些工具，比如我个人之前开发的<a href="https://tonybai.com/2020/12/10/a-kind-of-thinking-about-how-to-trace-function-call-chain">函数调用跟踪工具</a>，查看程序执行过程中的函数调用次序。</p>
<p>更细节的内容，还是要深入到代码中去查看。</p>
<ul>
<li>对Go项目中的一些设计模式的看法？如何使用设计模式，使用时注意哪些事项？</li>
</ul>
<p>设计模式在go语言中并不是一个经常拿出来提的东西。我之前的一个观点：在其他语言中，需要大家通过一些额外细心的设计构建出来的设计模式，在Go语言中是自然而然就有的东西。</p>
<p>我在自己的日常编码过程中，不会太多从如何应用设计模式的角度思考，而是按照go设计哲学，去考虑并发设计、组合的设计，而不是非要套用那23个经典设计模式。</p>
<hr />
<p><a href="https://wx.zsxq.com/dweb2/index/group/51284458844544">“Gopher部落”知识星球</a>旨在打造一个精品Go学习和进阶社群！高品质首发Go技术文章，“三天”首发阅读权，每年两期Go语言发展现状分析，每天提前1小时阅读到新鲜的Gopher日报，网课、技术专栏、图书内容前瞻，六小时内必答保证等满足你关于Go语言生态的所有需求！2022年，Gopher部落全面改版，将持续分享Go语言与Go应用领域的知识、技巧与实践，并增加诸多互动形式。欢迎大家加入！</p>
<p><img src="http://image.tonybai.com/img/tonybai/gopher-tribe-zsxq-small-card.png" alt="img{512x368}" /><br />
<img src="http://image.tonybai.com/img/tonybai/go-programming-from-beginner-to-master-qr.png" alt="img{512x368}" /></p>
<p><img src="http://image.tonybai.com/img/tonybai/go-first-course-banner.png" alt="img{512x368}" /><br />
<img src="http://image.tonybai.com/img/tonybai/imooc-go-column-pgo-with-qr.jpg" alt="img{512x368}" /></p>
<p><a href="https://tonybai.com/">我爱发短信</a>：企业级短信平台定制开发专家 https://tonybai.com/。smspush : 可部署在企业内部的定制化短信平台，三网覆盖，不惧大并发接入，可定制扩展； 短信内容你来定，不再受约束, 接口丰富，支持长短信，签名可选。2020年4月8日，中国三大电信运营商联合发布《5G消息白皮书》，51短信平台也会全新升级到“51商用消息平台”，全面支持5G RCS消息。</p>
<p>著名云主机服务厂商DigitalOcean发布最新的主机计划，入门级Droplet配置升级为：1 core CPU、1G内存、25G高速SSD，价格5$/月。有使用DigitalOcean需求的朋友，可以打开这个<a href="https://m.do.co/c/bff6eed92687">链接地址</a>：https://m.do.co/c/bff6eed92687 开启你的DO主机之路。</p>
<p>Gopher Daily(Gopher每日新闻)归档仓库 &#8211; https://github.com/bigwhite/gopherdaily</p>
<p>我的联系方式：</p>
<ul>
<li>微博：https://weibo.com/bigwhite20xx</li>
<li>博客：tonybai.com</li>
<li>github: https://github.com/bigwhite</li>
</ul>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。</p>
<p style='text-align:left'>&copy; 2022, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2022/07/07/gocn-community-go-book-club-issue2-go-programming-from-beginner-to-master/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Golang的演化历程</title>
		<link>https://tonybai.com/2014/10/25/golang-history/</link>
		<comments>https://tonybai.com/2014/10/25/golang-history/#comments</comments>
		<pubDate>Sat, 25 Oct 2014 11:36:10 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[ANSI-C]]></category>
		<category><![CDATA[BCPL]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[Channel]]></category>
		<category><![CDATA[Concurrency]]></category>
		<category><![CDATA[CSP]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[Package]]></category>
		<category><![CDATA[RobPike]]></category>
		<category><![CDATA[UTF8]]></category>
		<category><![CDATA[包]]></category>
		<category><![CDATA[并发编程]]></category>
		<category><![CDATA[标准C]]></category>
		<category><![CDATA[标准库]]></category>
		<category><![CDATA[素数筛]]></category>

		<guid isPermaLink="false">http://tonybai.com/?p=1585</guid>
		<description><![CDATA[本文来自Google的Golang语言设计者之一Rob Pike大神在GopherCon2014大会上的开幕主题演讲资料&#8220;Hello, Gophers!&#8221;。Rob大神在这次分 享中用了两个生动的例子讲述了Golang的演化历程，总结了Golang到目前为止的成功因素，值得广大Golang Programmer &#38; Beginner学习和了解。这里也用了&#34;Golang的演化历程&#34;作为标题。 1、Hello Gophers! package main import &#34;fmt&#34; func main() { &#160;&#160;&#160; fmt.Printf(&#34;Hello, gophers!\n&#34;) } Rob大神的见面礼，后续会有针对这段的演化历史的陈述。 2、历史 这是一个历史性的时刻。 Golang已经获得了一定的成功，值得拥有属于自己的技术大会。 3、成功 促成这份成功的因素有许多： &#160;&#160;&#160; &#8211; 功能 &#160;&#160;&#160; &#8211; 缺少的功能 &#160;&#160;&#160; &#8211; 功能的组合 &#160;&#160;&#160; &#8211; 设计&#160;&#160;&#160; &#160;&#160;&#160; &#8211; 人 &#160;&#160;&#160; &#8211; 时间 4、案例学习：两段程序 我们来近距离回顾两段程序。 第一个是你见过的第一个Go程序，是属于你的历史时刻。 第二个是我们见过的第一个Go程序，是属于全世界所有Gophers的历史时刻。 先看第一个&#8220;hello, world&#8221; 5、hello.b main( ) { &#160;&#160;&#160; [...]]]></description>
			<content:encoded><![CDATA[<p>本文来自Google的<a href="http://golang.org">Golang</a>语言设计者之一<a href="http://en.wikipedia.org/wiki/Rob_Pike">Rob Pike</a>大神在GopherCon2014大会上的开幕主题演讲资料&ldquo;<a href="http://talks.golang.org/2014/hellogophers.slide">Hello, Gophers</a>!&rdquo;。Rob大神在这次分 享中用了两个生动的例子讲述了Golang的演化历程，总结了Golang到目前为止的成功因素，值得广大Golang Programmer &amp; Beginner学习和了解。这里也用了&quot;Golang的演化历程&quot;作为标题。</p>
<p><b>1、Hello Gophers!</b></p>
<p><font face="Courier New">package main</font></p>
<p><font face="Courier New">import &quot;fmt&quot;</font></p>
<p><font face="Courier New">func main() {<br />
	&nbsp;&nbsp;&nbsp; fmt.Printf(&quot;Hello, gophers!\n&quot;)<br />
	}</font></p>
<p>Rob大神的见面礼，后续会有针对这段的演化历史的陈述。</p>
<p><b>2、历史</b></p>
<p>这是一个历史性的时刻。</p>
<p>Golang已经获得了一定的成功，值得拥有属于自己的技术大会。</p>
<p><b>3、成功</b></p>
<p>促成这份成功的因素有许多：</p>
<p>&nbsp;&nbsp;&nbsp; &#8211; 功能<br />
	&nbsp;&nbsp;&nbsp; &#8211; 缺少的功能<br />
	&nbsp;&nbsp;&nbsp; &#8211; 功能的组合<br />
	&nbsp;&nbsp;&nbsp; &#8211; 设计&nbsp;&nbsp;&nbsp;<br />
	&nbsp;&nbsp;&nbsp; &#8211; 人<br />
	&nbsp;&nbsp;&nbsp; &#8211; 时间</p>
<p><b>4、案例学习：两段程序</b></p>
<p>我们来近距离回顾两段程序。</p>
<p>第一个是你见过的第一个Go程序，是属于你的历史时刻。<br />
	第二个是我们见过的第一个Go程序，是属于全世界所有Gophers的历史时刻。</p>
<p>先看第一个&ldquo;hello, world&rdquo;</p>
<p><b>5、hello.b</b></p>
<p><font face="Courier New">main( ) {<br />
	&nbsp;&nbsp;&nbsp; extrn a, b, c;<br />
	&nbsp;&nbsp;&nbsp; putchar(a); putchar(b); putchar(c); putchar(&#39;!*n&#39;);<br />
	}<br />
	a &#39;hell&#39;;<br />
	b &#39;o, w&#39;;<br />
	c &#39;orld&#39;;</font></p>
<p>上面这段代码首先出现在1972年<a href="http://en.wikipedia.org/wiki/Brian_Kernighan">Brian W. Kernighan</a>的B语言教程中（也有另外一说是出现在那之前的BCPL语言中）。</p>
<p><b>6、hello.c</b></p>
<p><font face="Courier New">main()<br />
	{<br />
	&nbsp;&nbsp;&nbsp; printf(&quot;hello, world&quot;);<br />
	}</font></p>
<p>上面这段代码出现在1974年Brian W. Kernighan编写的《Programming in C: A Tutorial》中。这份教程当时是作为Unix v5文档的一部分。</p>
<p><b>7、hello.c </b></p>
<p><font face="Courier New">main()<br />
	{<br />
	&nbsp;&nbsp;&nbsp; printf(&quot;hello, world\n&quot;); //译注：与上面的hello.c相比，多了个换行符\n输出<br />
	}</font></p>
<p>这段代码首次出现在1978年Brian W. Kernighan和<a href="http://en.wikipedia.org/wiki/Dennis_Ritchie">Dennis M. Ritchie</a>合著的《<a href="http://book.douban.com/subject/4816029/">The C Programming Language</a>》一书中。</p>
<p><b>8、hello.c, 标准C草案</b></p>
<p><font face="Courier New">#include &lt;stdio.h&gt; //译注：与上面hello.c相比， 多了这个头文件包含</font></p>
<p><font face="Courier New">main()<br />
	{<br />
	&nbsp;&nbsp;&nbsp; printf(&quot;hello, world\n&quot;);<br />
	}</font></p>
<p>这段代码出现在1988年Brian W. Kernighan和Dennis M. Ritchie合著的《<a href="http://book.douban.com/subject/1236999/">The C Programming Language</a>》第二版一书中，基于标准C草案。</p>
<p>9、hello.c，标准C89</p>
<p><font face="Courier New">#include &lt;stdio.h&gt;</font></p>
<p><font face="Courier New">main(void) //</font><font face="Courier New"><font face="Courier&lt;br /&gt;<br />
        New">译注：与上面hello.c相比，多了个void</font><br />
	{<br />
	&nbsp;&nbsp;&nbsp; printf(&quot;hello, world\n&quot;);<br />
	}</font></p>
<p>这段代码出现在1988年Brian W. Kernighan和Dennis M. Ritchie合著的《The C Programming Language》第二版第二次修订中。</p>
<p><b>10、一两代之后&#8230;</b></p>
<p>(省略所有中间语言)</p>
<p>关于Golang的讨论开始于2007年年末。</p>
<p>第一版语言规范起草于2008年3月份。</p>
<p>用于实验和原型目的的编译器开发工作已经展开。</p>
<p>最初的编译器输出的是C代码。</p>
<p>语言规范一形成，我们就重写了编译器，输出本地代码（机器码）。</p>
<p><b>11、hello.go, 2008年6月6日</b></p>
<p><font face="Courier New">package main</font></p>
<p><font face="Courier New">func main() int {<br />
	&nbsp;&nbsp;&nbsp; print &quot;hello, world\n&quot;;<br />
	&nbsp;&nbsp;&nbsp; return 0;<br />
	}</font></p>
<p>针对首次提交代码的测试。</p>
<p>内置的print已经是当时的全部实现。main函数返回一个int类型值。<br />
	注意：print后面没有括号。</p>
<p><b>12、hello.go，2008年6月27日</b></p>
<p><font face="Courier New">package main</font></p>
<p><font face="Courier New">func main() {<br />
	&nbsp;&nbsp;&nbsp; print &quot;hello, world\n&quot;;<br />
	}</font></p>
<p>当main函数返回，程序调用exit(0)。</p>
<p><b>13、hello.go，2008年8月11日</b></p>
<p><font face="Courier New">package main</font></p>
<p><font face="Courier New">func main() {<br />
	&nbsp;&nbsp;&nbsp; print(&quot;hello, world\n&quot;);<br />
	}</font></p>
<p>print调用加上了括号，这时print是一个函数，不再是一个原语。</p>
<p><b>14、hello.go，2008年10月24日</b></p>
<p><font face="Courier New">package main</font></p>
<p><font face="Courier New">import &quot;fmt&quot;</font></p>
<p><font face="Courier New">func main() {<br />
	&nbsp;&nbsp;&nbsp; fmt.printf(&quot;hello, world\n&quot;);<br />
	}</font></p>
<p>我们熟知并喜欢的printf来了。</p>
<p><b>15、hello.go，2009年1月15日</b></p>
<p><font face="Courier New">package main</font></p>
<p><font face="Courier New">import &quot;fmt&quot;</font></p>
<p><font face="Courier New">func main() {<br />
	&nbsp;&nbsp;&nbsp; fmt.Printf(&quot;hello, world\n&quot;);<br />
	}</font></p>
<p>头母大写的函数名用作才是导出的符号。</p>
<p>16、hello.go, 2009年12约11日</p>
<p><font face="Courier New">package main</font></p>
<p><font face="Courier New">import &quot;fmt&quot;</font></p>
<p><font face="Courier New">func main() {<br />
	&nbsp;&nbsp;&nbsp; fmt.Printf(&quot;hello, world\n&quot;)<br />
	}</font></p>
<p>不再需要分号。</p>
<p>这是在2009年11月10日Golang开发发布后的一次重要改变。</p>
<p>这也是当前版本的hello, world</p>
<p>我们花了些时间到达这里（32年！）</p>
<p>都是历史了！</p>
<p><b>17、不仅仅有C</b></p>
<p>我们从&quot;C&quot;开始，但Go与C相比有着巨大的不同。</p>
<p>其他一些语言影响和贯穿于Go的设计当中。</p>
<p><font face="Courier New">&nbsp;&nbsp;&nbsp; C: 语句和表达式语法<br />
	&nbsp;&nbsp;&nbsp; Pascal: 声明语法<br />
	&nbsp;&nbsp;&nbsp; Modula 2, Oberon 2：包<br />
	&nbsp;&nbsp;&nbsp; CSP, Occam, Newsqueak, Limbo, Alef:&nbsp; 并发<br />
	&nbsp;&nbsp;&nbsp; BCPL: 分号规则<br />
	&nbsp;&nbsp;&nbsp; Smalltalk: 方法(method)<br />
	&nbsp;&nbsp;&nbsp; Newsqueak: &lt;-, :=<br />
	&nbsp;&nbsp;&nbsp; APL: iota</font><br />
	&nbsp;&nbsp;&nbsp;<br />
	等等。也有一些是全新发明的，例如defer、常量。</p>
<p>还有一些来自其他语言的优点和缺点：<br />
	<font face="Courier New">&nbsp;&nbsp;&nbsp; C++, C#, Java, JavaScript, LISP, Python, Scala, &#8230;</font></p>
<p><b>18、hello.go，Go 1版</b></p>
<p>将我们带到了今天。</p>
<p><font face="Courier New">package main</font></p>
<p><font face="Courier New">import &quot;fmt&quot;</font></p>
<p><font face="Courier New">func main() {<br />
	&nbsp;&nbsp;&nbsp; fmt.Println(&quot;Hello, Gophers (some of whom know 中文)!&quot;)<br />
	}</font></p>
<p>我们来深入挖掘一下，把这段代码做一个拆解。</p>
<p><b>19、Hello, World的16个tokens</b></p>
<p><font face="Courier New">package<br />
	main<br />
	import<br />
	&quot;fmt&quot;<br />
	func<br />
	main<br />
	(<br />
	)<br />
	{<br />
	fmt<br />
	.<br />
	Println<br />
	(<br />
	&quot;Hello, Gophers (some of whom know 中文)!&quot;<br />
	)<br />
	}</font></p>
<p><b>20、package</b></p>
<p>早期设计讨论的主要话题：扩展性的关键</p>
<p>package是什么？来自Modula-2等语言的idea</p>
<p>为什么是package?</p>
<p>&nbsp;&nbsp;&nbsp; &#8211; 拥有编译构建所需的全部信息<br />
	&nbsp;&nbsp;&nbsp; &#8211; 没有循环依赖(import)<br />
	&nbsp;&nbsp;&nbsp; &#8211; 没有子包<br />
	&nbsp;&nbsp;&nbsp; &#8211; 包名与包路径分离<br />
	&nbsp;&nbsp;&nbsp; &#8211; 包级别可见性，而不是类型级别<br />
	&nbsp;&nbsp;&nbsp; &#8211; 在包内部，你拥有整个语言，在包外部，你只拥有包许可的东西。</p>
<p><b>21、main</b></p>
<p>一个C语言遗留风范尽显之处<br />
	最初是Main，原因记不得了。<br />
	主要的包，main函数<br />
	很特别，因为它是初始化树(initialization tree)的根(root)。</p>
<p><b>22、import</b></p>
<p>一种加载包的机制<br />
	通过编译器实现（有别于文本预处理器。译注：C语言的include是通过preprocessor实现的）<br />
	努力使其高效且线性<br />
	导入的一个包，而不是一个标识符(identifiers)集合（译注：C语言的include是将头文件里的标识符集合引入）<br />
	至于export，它曾经是一个关键字。</p>
<p><b>23、&quot;fmt&quot;</b></p>
<p>包路径(package path)只是一个字符串，并非标识符的列表。<br />
	让语言避免定义它的含义 &#8211; 适应性。(Allows the language to avoid defining what it means&mdash;adaptability)<br />
	从一开始就想要一个URL作为一个选项。（译注：类似import &quot;github.com/go/tools/xxx）<br />
	可以应付将来的发展。</p>
<p><b>24、func</b></p>
<p>一个关键字，用于引入函数(类型、变量、常量），易于编译器解析。<br />
	对于函数字面量(闭包)而言，易于解析非常重要。<br />
	顺便说一下，最初这个关键字不是func，而是function。</p>
<p>小插曲：</p>
<p>Mail thread from February 6, 2008<br />
	From: Ken Thompson <a class="moz-txt-link-rfc2396E" href="mailto:ken@google.com">&lt;ken@google.com&gt;</a>  <br />
	To: gri, r<br />
	larry and sergey came by tonight. we  talked about go for more than an hour.  they both said they liked it very much.<br />
	p.s. one of larrys comments was &quot;why isnt function spelled func?&quot;<br />
	&#8212;<br />
	From: Rob Pike <a class="moz-txt-link-rfc2396E" href="mailto:r@google.com">&lt;r@google.com&gt;</a><br />
	 To: ken, gri<br />
	fine with me. seems compatible with &#39;var&#39;.<br />
	anyway we can always say, &quot;larry said to call it &#39;func&#39;&quot;</p>
<p><b>25、main</b></p>
<p>程序执行的起点。除非它不是。（译注：main不是起点，rob大神的意思是不是指下列情形，比如go test测试包，在google app engine上的go程序不需要main）<br />
	将初始化与正常执行分离，早期计划之中的。<br />
	初始化在哪里发生的？(译注：说的是package内的func init() {..}函数吧)<br />
	回到包设计。（译注：重温golang的package设计思想）</p>
<p><b>26、()</b></p>
<p>看看，没有void<br />
	main没有返回值，由运行时来处理main的返回后的事情。<br />
	没有函数参数（命令行选项通过os包获取）<br />
	没有返回值</p>
<p>返回值以及语法</p>
<p><b>27、{</b></p>
<p>用的是大括号，而不是空格（译注：估计是与python的空格缩进对比）<br />
	同样也不是方括号。<br />
	为什么在括号后放置换行符(newline)？</p>
<p><b>28、</b><b>fmt</b></p>
<p>所有导入的标识符均限定于其导入的包。（All imported identifiers are qualified by their import.）<br />
	每个标识符要么是包或函数的本地变量，要么被类型或导入包限定。<br />
	对代码可读性的重大影响。</p>
<p>为什么是fmt，而不是format？</p>
<p><b>29、.</b></p>
<p>句号token在Go中有多少使用？（很多）<br />
	a.B的含义需要使用到类型系统<br />
	但这对于人类来说非常清晰，读起来也非常容易。<br />
	针对指针的自动转换(没有-&gt;)。</p>
<p><b>30、Println</b></p>
<p>Println，不是println，头母大写才是导出符号。<br />
	知道它是反射驱动的(reflection-driven)<br />
	可变参数函数<br />
	参数类型是(&#8230;); 2010年2月1日变成(&#8230;interface{})</p>
<p><b>31、(</b></p>
<p>传统函数语法</p>
<p><b>32、</b><b><font face="Courier New">&quot;Hello, Gophers (some of whom know 中文)!&quot;</font></b></p>
<p>UTF-8编码的源码输入。字符串字面量也自动是utf8编码格式的。</p>
<p>但什么是字符串(string)呢？</p>
<p>首批写入规范的语法规则，今天很难改变了。(blog.golang.org/strings)</p>
<p><b>33、)</b></p>
<p>没有分号<br />
	在go发布后不久我们就去除了分号<br />
	早期曾胡闹地尝试将它们（译注：指得是括号）去掉<br />
	最终接受了BCPL的方案</p>
<p><b>34、}</b></p>
<p>第一轮结束。</p>
<p>旁白：还没有讨论到的</p>
<p>&nbsp;&nbsp;&nbsp; &#8211; 类型<br />
	&nbsp;&nbsp;&nbsp; &#8211; 常量<br />
	&nbsp;&nbsp;&nbsp; &#8211; 方法<br />
	&nbsp;&nbsp;&nbsp; &#8211; interface<br />
	&nbsp;&nbsp;&nbsp; &#8211; 库<br />
	&nbsp;&nbsp;&nbsp; &#8211; 内存管理<br />
	&nbsp;&nbsp;&nbsp; &#8211; 并发（接下来将讨论）<br />
	&nbsp;&nbsp;&nbsp;<br />
	外加工具，生态系统，社区等。<br />
	语言是核心，但也只是我们故事的一部分。</p>
<p><b>35、成功</b></p>
<p>要素：<br />
	&nbsp;&nbsp;&nbsp; &#8211; 站在巨人的肩膀上(building on history)<br />
	&nbsp;&nbsp;&nbsp; &#8211; 经验之作(building on experience) 译注：最初的三个神级语言设计者<br />
	&nbsp;&nbsp;&nbsp; &#8211; 设计过程<br />
	&nbsp;&nbsp;&nbsp; &#8211; 早期idea提炼到最终的方案中<br />
	&nbsp;&nbsp;&nbsp; &#8211; 由一个小团队专门集中精力做<br />
	&nbsp;&nbsp;&nbsp;<br />
	最终：承诺<br />
	&nbsp;&nbsp;&nbsp; Go 1.0锁定了语言核心与标准库。</p>
<p><b>36、另一轮</b></p>
<p>让我们看第二个程序的类似演化过程。</p>
<p><b>37、问题：素数筛(Prime sieve)</b></p>
<p>问题来自于Communicating Sequential Processes, by C. A. R. Hoare, 1978。</p>
<p>&ldquo;问题：以升序打印所有小于10000的素数。使用一个process数组：SIEVE，其中每个process从其前驱元素输入一个素数并打印它。接下 来这个process从其前驱元素接收到一个升序数字流并将它们传给其后继元素，这个过程会剔除掉所有是最初素数整数倍的数字。</p>
<p><b>38、解决方案</b></p>
<p>在1978年的CSP论文中。（注意不是Eratosthenes筛）</p>
<p>这个优美的方案是由David Gries贡献出来的。</p>
<p><b>39、CSP</b></p>
<p>在Hoare的CSP论文中：</p>
<p><font face="Courier New">[SIEVE(i:1..100)::<br />
	&nbsp;&nbsp;&nbsp; p,mp:integer;<br />
	&nbsp;&nbsp;&nbsp; SIEVE(i - 1)?p;<br />
	&nbsp;&nbsp;&nbsp; print!p;<br />
	&nbsp;&nbsp;&nbsp; mp := p; comment mp is a multiple of p;<br />
	*[m:integer; SIEVE(i - 1)?m &rarr;<br />
	&nbsp;&nbsp;&nbsp; *[m > mp &rarr; mp := mp + p];<br />
	&nbsp;&nbsp;&nbsp; [m = mp &rarr; skip<br />
	&nbsp;&nbsp;&nbsp; ||m &lt; mp &rarr; SIEVE(i + 1)!m<br />
	]&nbsp;&nbsp; ]<br />
	||SIEVE(0)::print!2; n:integer; n := 3;<br />
	&nbsp;&nbsp;&nbsp; *[n < 10000 &rarr; SIEVE(1)!n; n := n + 2]<br />
	||SIEVE(101)::*[n:integer;SIEVE(100)?n &rarr; print!n]<br />
	||print::*[(i:0..101) n:integer; SIEVE(i)?n &rarr; ...]<br />
	] </font></p>
<p>没有channel。能处理的素数的个数是在程序中指定的。</p>
<p><b>40、Newsqueak</b></p>
<p>circa 1988。</p>
<p>Rob Pike语言设计，Tom Cargill和Doug McIlroy实现。</p>
<p>使用了channels，这样个数是可编程的。(channel这个idea从何而来？）</p>
<p><font face="Courier New">counter:=prog(end: int, c: chan of int)<br />
	{<br />
	&nbsp;&nbsp;&nbsp; i:int;<br />
	&nbsp;&nbsp;&nbsp; for(i=2; i&lt;end; i++)<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; c&lt;-=i;<br />
	};</font></p>
<p><font face="Courier New">filter:=prog(prime: int, listen: chan of int, send: chan of int)<br />
	{<br />
	&nbsp;&nbsp;&nbsp; i:int;<br />
	&nbsp;&nbsp;&nbsp; for(;;)<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if((i=&lt;-listen)%prime)<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; send&lt;-=i;<br />
	};</font></p>
<p><font face="Courier New">sieve:=prog(c: chan of int)<br />
	{<br />
	&nbsp;&nbsp;&nbsp; for(;;){<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; prime:=&lt;-c;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print(prime, &quot; &quot;);<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; newc:=mk(chan of int);<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; begin filter(prime, c, newc);<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; c=newc;<br />
	&nbsp;&nbsp;&nbsp; }<br />
	};</font></p>
<p><font face="Courier New">count:=mk(chan of int);</font></p>
<p><font face="Courier New">begin counter(10000, count);<br />
	begin sieve(count);<br />
	&quot;&quot;;</font></p>
<p><b>41、sieve.go，2008年3月5日</b></p>
<p>使用go规范编写的第一个版本，可能是第二个由go编写的重要程序。</p>
<p>&gt;用于发送；&lt;用于接收。Channel是指针。Main是头字母大写的。</p>
<p><font face="Courier New">package Main</font></p>
<p><font face="Courier New">// Send the sequence 2, 3, 4, &#8230; to channel &#39;ch&#39;.<br />
	func Generate(ch *chan&gt; int) {<br />
	&nbsp;&nbsp;&nbsp; for i := 2; ; i++ {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &gt;ch = i;&nbsp;&nbsp;&nbsp; // Send &#39;i&#39; to channel &#39;ch&#39;.<br />
	&nbsp;&nbsp;&nbsp; }<br />
	}</font></p>
<p><font face="Courier New">// Copy the values from channel &#39;in&#39; to channel &#39;out&#39;,<br />
	// removing those divisible by &#39;prime&#39;.<br />
	func Filter(in *chan&lt; int, out *chan&gt; int, prime int) {<br />
	&nbsp;&nbsp;&nbsp; for ; ; {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; i := &lt;in;&nbsp;&nbsp;&nbsp; // Receive value of new variable &#39;i&#39; from &#39;in&#39;.<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if i % prime != 0 {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &gt;out = i;&nbsp;&nbsp;&nbsp; // Send &#39;i&#39; to channel &#39;out&#39;.<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	&nbsp;&nbsp;&nbsp; }<br />
	}</font></p>
<p><font face="Courier New">// The prime sieve: Daisy-chain Filter processes together.<br />
	func Sieve() {<br />
	&nbsp;&nbsp;&nbsp; ch := new(chan int);&nbsp; // Create a new channel.<br />
	&nbsp;&nbsp;&nbsp; go Generate(ch);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Start Generate() as a subprocess.<br />
	&nbsp;&nbsp;&nbsp; for ; ; {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; prime := &lt;ch;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf(&quot;%d\n&quot;, prime);<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ch1 := new(chan int);<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; go Filter(ch, ch1, prime);<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ch = ch1;<br />
	&nbsp;&nbsp;&nbsp; }<br />
	}</font></p>
<p><font face="Courier New">func Main() {<br />
	&nbsp;&nbsp;&nbsp; Sieve();<br />
	}</font></p>
<p><b>42. sieve.go，2008年7月22日</b></p>
<p><font face="Courier New">-&lt;</font>用于发送；<font face="Courier New">-&lt;</font>用于接收。Channel仍然是指针。但现在main不是大写字母开头的了。</p>
<p><font face="Courier New">package main</font></p>
<p><font face="Courier New">// Send the sequence 2, 3, 4, &#8230; to channel &#39;ch&#39;.<br />
	func Generate(ch *chan-&lt; int) {<br />
	&nbsp;&nbsp;&nbsp; for i := 2; ; i++ {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ch -&lt; i&nbsp;&nbsp;&nbsp; // Send &#39;i&#39; to channel &#39;ch&#39;.<br />
	&nbsp;&nbsp;&nbsp; }<br />
	}</font></p>
<p><font face="Courier New">// Copy the values from channel &#39;in&#39; to channel &#39;out&#39;,<br />
	// removing those divisible by &#39;prime&#39;.<br />
	func Filter(in *chan&lt;- int, out *chan-&lt; int, prime int) {<br />
	&nbsp;&nbsp;&nbsp; for {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; i := &lt;-in;&nbsp;&nbsp;&nbsp; // Receive value of new variable &#39;i&#39; from &#39;in&#39;.<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if i % prime != 0 {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out -&lt; i&nbsp;&nbsp;&nbsp; // Send &#39;i&#39; to channel &#39;out&#39;.<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	&nbsp;&nbsp;&nbsp; }<br />
	}</font></p>
<p><font face="Courier New">// The prime sieve: Daisy-chain Filter processes together.<br />
	func Sieve() {<br />
	&nbsp;&nbsp;&nbsp; ch := new(chan int);&nbsp; // Create a new channel.<br />
	&nbsp;&nbsp;&nbsp; go Generate(ch);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Start Generate() as a subprocess.<br />
	&nbsp;&nbsp;&nbsp; for {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; prime := &lt;-ch;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf(&quot;%d\n&quot;,&nbsp;&nbsp;&nbsp; prime);<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ch1 := new(chan int);<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; go Filter(ch, ch1, prime);<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ch = ch1<br />
	&nbsp;&nbsp;&nbsp; }<br />
	}</font></p>
<p><font face="Courier New">func main() {<br />
	&nbsp;&nbsp;&nbsp; Sieve()<br />
	}</font></p>
<p><b>43、sieve.go，2008年9月17日</b></p>
<p>通信操作符现在是<font face="Courier New">&lt;-</font>。channel仍然是指针。</p>
<p><font face="Courier New">package main</font></p>
<p><font face="Courier New">// Send the sequence 2, 3, 4, &#8230; to channel &#39;ch&#39;.<br />
	func Generate(ch *chan &lt;- int) {<br />
	&nbsp;&nbsp;&nbsp; for i := 2; ; i++ {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ch &lt;- i&nbsp; // Send &#39;i&#39; to channel &#39;ch&#39;.<br />
	&nbsp;&nbsp;&nbsp; }<br />
	}</font></p>
<p><font face="Courier New">// Copy the values from channel &#39;in&#39; to channel &#39;out&#39;,<br />
	// removing those divisible by &#39;prime&#39;.<br />
	func Filter(in *chan &lt;- int, out *&lt;-chan int, prime int) {<br />
	&nbsp;&nbsp;&nbsp; for {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; i := &lt;-in;&nbsp; // Receive value of new variable &#39;i&#39; from &#39;in&#39;.<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if i % prime != 0 {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out &lt;- i&nbsp; // Send &#39;i&#39; to channel &#39;out&#39;.<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	&nbsp;&nbsp;&nbsp; }<br />
	}</font></p>
<p><font face="Courier New">// The prime sieve: Daisy-chain Filter processes together.<br />
	func Sieve() {<br />
	&nbsp;&nbsp;&nbsp; ch := new(chan int);&nbsp; // Create a new channel.<br />
	&nbsp;&nbsp;&nbsp; go Generate(ch);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Start Generate() as a subprocess.<br />
	&nbsp;&nbsp;&nbsp; for {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; prime := &lt;-ch;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print(prime, &quot;\n&quot;);<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ch1 := new(chan int);<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; go Filter(ch, ch1, prime);<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ch = ch1<br />
	&nbsp;&nbsp;&nbsp; }<br />
	}</font></p>
<p><font face="Courier New">func main() {<br />
	&nbsp;&nbsp;&nbsp; Sieve()<br />
	}</font></p>
<p><b><font face="Courier New">44、sieve.go，2009年1月6日</font></b></p>
<p>引入了make内置操作符。没有指针。编码错误！（有个*被留下了，错误的参数类型）</p>
<p><font face="Courier New">package main</font></p>
<p><font face="Courier New">// Send the sequence 2, 3, 4, &#8230; to channel &#39;ch&#39;.<br />
	func Generate(ch chan &lt;- int) {<br />
	&nbsp;&nbsp;&nbsp; for i := 2; ; i++ {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ch &lt;- i&nbsp; // Send &#39;i&#39; to channel &#39;ch&#39;.<br />
	&nbsp;&nbsp;&nbsp; }<br />
	}</font></p>
<p><font face="Courier New">// Copy the values from channel &#39;in&#39; to channel &#39;out&#39;,<br />
	// removing those divisible by &#39;prime&#39;.<br />
	func Filter(in chan &lt;- int, out *&lt;-chan int, prime int) {<br />
	&nbsp;&nbsp;&nbsp; for {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; i := &lt;-in;&nbsp; // Receive value of new variable &#39;i&#39; from &#39;in&#39;.<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if i % prime != 0 {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out &lt;- i&nbsp; // Send &#39;i&#39; to channel &#39;out&#39;.<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	&nbsp;&nbsp;&nbsp; }<br />
	}</font></p>
<p><font face="Courier New">// The prime sieve: Daisy-chain Filter processes together.<br />
	func Sieve() {<br />
	&nbsp;&nbsp;&nbsp; ch := make(chan int);&nbsp; // Create a new channel.<br />
	&nbsp;&nbsp;&nbsp; go Generate(ch);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Start Generate() as a subprocess.<br />
	&nbsp;&nbsp;&nbsp; for {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; prime := &lt;-ch;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print(prime, &quot;\n&quot;);<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ch1 := make(chan int);<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; go Filter(ch, ch1, prime);<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ch = ch1<br />
	&nbsp;&nbsp;&nbsp; }<br />
	}</font></p>
<p><font face="Courier New">func main() {<br />
	&nbsp;&nbsp;&nbsp; Sieve()<br />
	}</font></p>
<p><b>45、sieve.go，2009年9月25日</b></p>
<p>第一个正确的现代版本。同样，大写头母不见了，使用了fmt。</p>
<p><font face="Courier New">package main</font></p>
<p><font face="Courier New">import &quot;fmt&quot;</font></p>
<p><font face="Courier New">// Send the sequence 2, 3, 4, &#8230; to channel &#39;ch&#39;.<br />
	func generate(ch chan&lt;- int) {<br />
	&nbsp;&nbsp;&nbsp; for i := 2; ; i++ {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ch &lt;- i;&nbsp;&nbsp;&nbsp; // Send &#39;i&#39; to channel &#39;ch&#39;.<br />
	&nbsp;&nbsp;&nbsp; }<br />
	}</font></p>
<p><font face="Courier New">// Copy the values from channel &#39;in&#39; to channel &#39;out&#39;,<br />
	// removing those divisible by &#39;prime&#39;.<br />
	func filter(src &lt;-chan int, dst chan&lt;- int, prime int) {<br />
	&nbsp;&nbsp;&nbsp; for i := range src {&nbsp;&nbsp;&nbsp; // Loop over values received from &#39;src&#39;.<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if i%prime != 0 {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dst &lt;- i;&nbsp;&nbsp;&nbsp; // Send &#39;i&#39; to channel &#39;dst&#39;.<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	&nbsp;&nbsp;&nbsp; }<br />
	}</font></p>
<p><font face="Courier New">// The prime sieve: Daisy-chain filter processes together.<br />
	func sieve() {<br />
	&nbsp;&nbsp;&nbsp; ch := make(chan int);&nbsp; // Create a new channel.<br />
	&nbsp;&nbsp;&nbsp; go generate(ch);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Start generate() as a subprocess.<br />
	&nbsp;&nbsp;&nbsp; for {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; prime := &lt;-ch;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fmt.Print(prime, &quot;\n&quot;);<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ch1 := make(chan int);<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; go filter(ch, ch1, prime);<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ch = ch1;<br />
	&nbsp;&nbsp;&nbsp; }<br />
	}</font></p>
<p><font face="Courier New">func main() {<br />
	&nbsp;&nbsp;&nbsp; sieve();<br />
	}</font></p>
<p><b>46、sieve.go，2009年12月10日</b></p>
<p>分号不见了。程序已经与现在一致了。</p>
<p><font face="Courier New">package main</font></p>
<p><font face="Courier New">import &quot;fmt&quot;</font></p>
<p><font face="Courier New">// Send the sequence 2, 3, 4, &hellip; to channel &#39;ch&#39;.<br />
	func generate(ch chan&lt;- int) {<br />
	&nbsp;&nbsp;&nbsp; for i := 2; ; i++ {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ch &lt;- i&nbsp; // Send &#39;i&#39; to channel &#39;ch&#39;.<br />
	&nbsp;&nbsp;&nbsp; }<br />
	}</font></p>
<p><font face="Courier New">// Copy the values from channel &#39;src&#39; to channel &#39;dst&#39;,<br />
	// removing those divisible by &#39;prime&#39;.<br />
	func filter(src &lt;-chan int, dst chan&lt;- int, prime int) {<br />
	&nbsp;&nbsp;&nbsp; for i := range src {&nbsp; // Loop over values received from &#39;src&#39;.<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if i%prime != 0 {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dst &lt;- i&nbsp; // Send &#39;i&#39; to channel &#39;dst&#39;.<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	&nbsp;&nbsp;&nbsp; }<br />
	}</font></p>
<p><font face="Courier New">// The prime sieve: Daisy-chain filter processes together.<br />
	func sieve() {<br />
	&nbsp;&nbsp;&nbsp; ch := make(chan int)&nbsp; // Create a new channel.<br />
	&nbsp;&nbsp;&nbsp; go generate(ch)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Start generate() as a subprocess.<br />
	&nbsp;&nbsp;&nbsp; for {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; prime := &lt;-ch<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fmt.Print(prime, &quot;\n&quot;)<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ch1 := make(chan int)<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; go filter(ch, ch1, prime)<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ch = ch1<br />
	&nbsp;&nbsp;&nbsp; }<br />
	}</font></p>
<p><font face="Courier New">func main() {<br />
	&nbsp;&nbsp;&nbsp; sieve()<br />
	}</font></p>
<p>这个优美的方案来自于几十年的设计过程。</p>
<p><b>47、旁边，没有讨论到的</b></p>
<p><font face="Courier New">select</font></p>
<p>真实并发程序的核心连接器（connector)<br />
	最初起源于Dijkstra的守卫命令(guarded command)<br />
	在Hoare的CSP理论实现真正并发。<br />
	经过Newsqueak、Alef、Limbo和其他语言改良后</p>
<p>2008年3月26日出现在Go版本中。</p>
<p>简单，澄清，语法方面的考虑。</p>
<p><b>48、稳定性</b></p>
<p>Sieve程序自从2009年末就再未改变过。&#8211; 稳定！</p>
<p>开源系统并不总是兼容和稳定的。</p>
<p>但，Go是。（兼容和稳定的）</p>
<p>这是Go成功的一个重要原因。</p>
<p><b>49、</b><b>趋势</b></p>
<p>图数据展示了Go 1.0发布后Go语言的爆发。</p>
<p><b>50、成功</b></p>
<p>Go成功的元素：</p>
<p>&nbsp;&nbsp;&nbsp; 显然的：功能和工具。</p>
<p><font face="Courier New">&nbsp;&nbsp;&nbsp; * 并发<br />
	&nbsp;&nbsp;&nbsp; * 垃圾回收<br />
	&nbsp;&nbsp;&nbsp; * 高效的实现<br />
	&nbsp;&nbsp;&nbsp; * 给人以动态类型体验的静态类型系统<br />
	&nbsp;&nbsp;&nbsp; * 丰富但规模有限的标准库<br />
	&nbsp;&nbsp;&nbsp; * 工具化<br />
	&nbsp;&nbsp;&nbsp; * gofmt<br />
	&nbsp;&nbsp;&nbsp; * 在大规模系统中的应用</font></p>
<p>&nbsp;&nbsp;&nbsp; 不那么显然的：过程</p>
<p>&nbsp;&nbsp;&nbsp; * 始终聚焦最初的目标<br />
	&nbsp;&nbsp;&nbsp; * 在冻结后的集中开发<br />
	&nbsp;&nbsp;&nbsp; * 小核心团队易于取得一致<br />
	&nbsp;&nbsp;&nbsp; * 社区的重要贡献<br />
	&nbsp;&nbsp;&nbsp; * 丰富的生态系统<br />
	&nbsp;&nbsp;&nbsp;<br />
	总之，开源社区共享了我们的使命，聚焦于为当今的世界设计一门语言。</p>
<p style='text-align:left'>&copy; 2014, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2014/10/25/golang-history/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
