<audio id="audio" title="开篇词 | 想吃透Go并发编程，你得这样学！" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/d5/ca/d5ed65cd1940e3e47618b07e924b9dca.mp3"></audio>

你好，我是晁岳攀，网名鸟窝。之前我在微博研发平台架构中心担任资深架构师，同时也是微服务框架rpcx的作者，欢迎来到“Go并发编程实战课”。

## 并发编程，为什么选Go？

为什么要学Go并发呢？我想先和你聊聊我和Go结缘的经历。

作为一位老程序员，我在清华同方、摩托罗拉、Comcast等公司，一直使用Java做项目开发。但是后来，我毅然抛弃了十几年的Java编程经验，投入到了Go语言的怀抱，为什么呢？

一句话，我被Go的简单高效所打动。它不仅部署方便，自带完善的工具链，特别是Go在处理并发场景上表现出的独特性能，更是让我着迷。

我们知道，Java语言的编码非常繁琐，为了应用设计模式而做了大量的冗长设计，而Go就不一样了。它提供了便利的并发编程方式，简简单单的Go语句，就可以创建多个goroutine执行并发任务。而且，Go还提供了独特的Channel类型，很容易实现goroutine之间的数据交流。所以，Go并发编程入门很容易，即使是初学者，要写一个使用goroutine异步输出“Hello World”的例子，也可以不费吹灰之力。

不过，和其他语言相比，Go微服务治理框架的发展还是比较晚的。当阿里出品的Java微服务框架Dubbo被广泛应用时，Go生态圈还没有微服务框架。

于是，四五年前，为了填补Go生态圈微服务化的缺失，我就用Go开发了一个微服务的框架rpcx。它既有类似标准rpc库的易用特点，又包含了非常丰富的服务治理的功能。而且，根据[benchmark测试](https://colobu.com/2020/01/21/benchmark-2019-spring-of-popular-rpc-frameworks/)，rpcx有着数一数二的性能，很多互联网企业（比如马蜂窝、百度等）都在使用。

在微博的四年时间里，我使用Go参与开发多个基础架构系统，并负责中国版权链、微博下一代的Redis集群系统、数据库资源云等系统的设计和开发工作。在多年的实战中，我遇见过各种各样的并发难题，积累了大量的高并发高吞吐的服务器开发经验，也梳理了一整套并发编程的知识体系。

2019年，astaxie（谢孟军）邀请我在Gopher China大会上做一个关于Go并发编程的分享。我准备了一份120页的[PPT](https://myslide.cn/slides/23014#)，全面地介绍了Go并发编程的基础内容，包括基本并发原语、扩展并发原语和Channel等。会后，现场的观众都说干货满满，希望我能提供无删改版的PPT。

后来，在Go爱好者的强烈要求下，我又在滴滴举办了一场Go并发编程的培训，详细地分享了我的并发编程心得和经验，包括各种并发原语的基本用法和实现机制。

结合我自己的开发经验，以及这些年的技术分享经历，我真切地感受到了这一点：**Go并发编程的重要性不容置疑。只要是使用Go开发的大型应用程序，并发是必然要采用的技术。**

但同时，我也了解到，很多人想要学习Go并发编程，却不知道该从何学起，也不知该如何精进。

## 学习Go并发编程，有哪些困难？

那学习Go并发会有哪些困难呢？我总结了一下，主要是有5大问题。

1. 在面对并发难题时，感觉无从下手，不知道**该用什么并发原语来解决问题**。
1. 如果多个并发原语都可以解决问题，那么，**究竟哪个是最优解呢**？比如说是用互斥锁，还是用Channel。
1. **不知道如何编排并发任务**。并发编程不像是传统的串行编程，程序的运行存在着很大的不确定性。这个时候，就会面临一个问题，**怎么才能让相应的任务按照你设想的流程运行呢**？
1. 有时候，按照正常理解的并发方式去实现的程序，结果莫名其妙就panic或者死锁了，**排查起来非常困难**。
1. **已知的并发原语都不能解决并发问题**，程序写起来异常复杂，而且代码混乱，容易出错。

每一位刚入门Go的程序员，在深入学习Go语言的时候，尤其是面对Go并发编程的时候，都会遇到这些问题。那么，具体该怎么学呢？

## 怎么提升Go并发编程能力？

学习这件事儿，最怕的就是不成体系，即使知识点之间是彼此独立的，也必定存在着联系。我们要做的，就是找出逻辑关系，拎出知识线。我认为，**关于Go并发编程，有两条主线，分别是知识主线和学习主线**。具体是啥意思呢？可以看下面的这张知识地图。

<img src="https://static001.geekbang.org/resource/image/81/e3/81fa1cfd8d39632d871baeedf4081ce3.jpg" alt="">

从图中可以看到，在知识主线层面，这门课程的核心内容设计了5个模块：

- **基本并发原语**：在这部分，我会介绍Mutex、RWMutex、Waitgroup、Cond、Pool、Context等标准库中的并发原语，这些都是传统的并发原语，在其它语言中也很常见，是我们在并发编程中常用的类型。
- **原子操作：**在这部分，我会介绍Go标准库中提供的原子操作。原子操作是其它并发原语的基础，学会了你就可以自己创造新的并发原语。
- **Channel**：Channel类型是Go语言独特的类型，因为比较新，所以难以掌握。但是别怕，我会带你全方位地学习Channel类型，你不仅能掌握它的基本用法，而且还能掌握它的处理场景和应用模式，避免踩坑。
- **扩展并发原语**：目前来看，Go开发组不准备在标准库中扩充并发原语了，但是还有一些并发原语应用广泛，比如信号量、SingleFlight、循环栅栏、ErrGroup等。掌握了它们，就可以在处理一些并发问题时，取得事半功倍的效果。
- **分布式并发原语**：分布式并发原语是应对大规模的应用程序中并发问题的并发类型。我主要会介绍使用etcd实现的一些分布式并发原语，比如Leader选举、分布式互斥锁、分布式读写锁、分布式队列等，在处理分布式场景的并发问题时，特别有用。

<img src="https://static001.geekbang.org/resource/image/7a/de/7af6e5216dd0cc449878b3949a3e81de.jpg" alt="">

沿着这条知识主线，我会带你建立起一个丰富的并发原语库。你可以把并发问题当成一个强大的敌人，而这些并发原语，就是我们的武器。每一种并发原语都有它的用处，你只有知道足够多的并发原语，才能灵活地应对各种场景。

那具体怎么掌握这些武器呢？课程的每一个模块都是独立的，它们之间没有任何依赖问题，你可以结合自己的实际情况，有重点地进行学习。如果你对Channel类型不是太熟悉，就可以先看Channel这个模块的内容；如果你已经非常熟悉标准库的并发原语了，就可以看看扩展并发原语和分布式并发原语的内容。

同时，在学习主线层面，主要是**四大步骤，包括基础用法、实现原理、易错场景、知名项目中的Bug**。每一个模块，我都会带着你按照这四个步骤来学习，目的就是带你熟知每一种并发原语的实现机制和适用场景。

Go中有一个大的方向，就是任务编排用Channel，共享资源保护用传统并发原语。在刚开始学习时，你可以基于这个原则去选择相应的并发原语，这是没错的。但是，如果你想要在Go并发编程的道路上向前走，就不能局限于这个原则。

实际上，针对同一种场景，也许存在很多并发原语都适用的情况，但是一定是有最合适的那一个。所以，你必须非常清楚每种并发原语的实现机制和适用场景，千万不要被网上的一些文章误导，万事皆用Channel。

而且，你还可以深入学习下Go并发原语的源代码。你会发现很多独到的设计，比如Mutex为了公平性考量的设计、sync.Map为提升性能做的设计，以及很多并发原语的异常状况的处理方式。尤其是这些异常状况，常常是并发编程中程序panic的原因。

所以，如果你能深入了解这些并发原语的实现，不但会提高你的编程能力，还能让你避免在开发中踩并发问题的坑。这个时候，你就达到精通的程度了。

如果没有做过大型并发项目，你可能还不太清楚并发原语的重要性。那么，我建议你先阅读一下课程中介绍的知名项目中犯的错，这也是这门课里我特别设计的一部分内容。通过理解这些Go大牛们犯的错误以及解决方案，你就可以积累一套避坑指南和应对之道。

有了这两条线的学习，我们就从广度和深度上掌握了Go并发编程的知识点。这些是不是就足够了呢？我们还可以更进一步，你要有野心能够创造出自己需要的并发原语。

这里的创造有两层含义。第一层是对既有的并发原语进行组合，使用两个、三个或者更多的并发原语去解决问题。比如说，我们可以通过信号量和WaitGroup组合成一个新的并发原语，这个并发原语可以使用有限个goroutine并发处理子任务。第二层含义是“无中生有”，根据已经掌握的并发原语的设计经验，创造出合适的新的并发原语，以应对一些特殊的并发问题。比如说，标准库中并没有信号量，你可以自己创造出这个类型。

达到了这一层，那就不得了了，可以说你对Go并发原语的掌握已经出神入化了。那想要达到这个程度是不是很难呢？确实不容易，不过我相信，如果你仔细学习了我们课程里的每一节课，心里牢牢地锚定3个目标：建立起一个丰富的并发原语库；熟知每一种并发原语的实现机制和适用场景；能够创造出自己需要的并发原语。达到了这3个目标，你就可以轻松地应对各种并发问题了。甚至可以说，你几乎能站在Go并发编程的顶端，成为大牛中的一员。

最后，我想说的是，Go并发编程的世界确实纷繁复杂，涉及到的内容非常多。你可以把它看作是一个江湖，如果你想拥有极强的作战力，就要拥有足够多的武器，并且修炼内功。这门课，就是你的修炼山洞，我准备了应有尽有的宝藏，等待着你来挖掘。

修炼的过程中，最好有人和你并肩而行，共同成长。欢迎你把这门课分享给你的朋友或同事，和他/她一起提升并发编程的功力。
