《 Go 语言并发之道》读后感 - 第二章

Go语言并发之道读后感-第二章

CSP (Communicating Sequential Processes)
我们可能听说过,通过通信去共享内存,Go pipeline 等,这一切的核心思想就是 CSP 理论。在这一章作者详尽的介绍了 CSP 与 Go 并发哲学的关系。

并发与并行的区别

并发属于代码,并行属于一个运行中的程序。

引用 Erlang 之父 Joe Armstrong 图

erlang-之父并发与并行

并发 = 两个队列一个咖啡机,两个队列的人交替使用咖啡机,可能会遇见如下情况:

A 队列被 B 队列小伙伴插队了,这就是条件竞争。

A 队列有一位英俊小伙和 B 队列一位女神并排,轮到他们俩的时候两人互看对方一眼,互让一步同时彬彬有礼的说了一句:你先。这个时候活锁出现了。

B 队列一位小伙伴,是代替其他朋友来打咖啡,他带了一箱子杯子,终于轮到他了。这时饥饿就出现了。

A 队列一位小伙伴喜欢摩卡,卡布奇诺混合咖啡,B 队列一位小伙伴喜欢拿铁,摩卡混合咖啡。当他们互相等待对方摩卡出杯的时候后,死锁来了。

并行 = 两个队列两台咖啡机。

编写正确的并发逻辑越难,越需要我们将很简单的并发原语组合起来使用。在Go 语言出现之前,大部分的主流编程语言都有一系列的抽象层。 如果你想写并发代码,你需要对你的程序按照线程同步以及内存访问同步来建模。如果你有一大堆需要并发建模的东西,而你的计算机又不能处理那么多的线程,就需要创建一个线程池,并将你的操作在线程池中复用。

Go 语言在这个联调中加入了新的一环:goroutine。另外,Go 语言从著名的计算机科学家 C.A.R. Hoare 那里借用了不少概念,并且给我们提供了新的原语来使用,即 channel。Go 语言并发原语的根基论文: C.A.R. Hoare 开创性的论文 “Communicating Sequential Processes”。

在 Go 语言中线程依旧存在,但是已经不需要我们去操心线程创建,复用,销毁,合并等操作,由 Go 语言本身完成。我们只需要使用更加简单的 goroutine 和 channel 即可。偶尔需要考虑一下共享内存的问题。

什么是 CSP

当进行和 Go 语言相关的讨论的时候,你经常会听到人们抛出 CSP。

CSP 是 “Communicating Sequential Processes" 的缩写,译为通信顺序进程。在 1978 年,C.A.R. Hoare 在国际计算机协会工作时发表的论文。

在这篇论文里,Hoare 认为输入与输出时两个被忽略的编程原语,尤其是在并发代码中。在 Hoare 写作这篇论文的同时,面向对象方式正在成为编程的基石,并发操作并没有被给与过多的思考。Hoare 开始纠正这个现象,在接下来的 6 年里,关于 CSP 的想法被提炼成了一个叫做 "进程微积分"的正式名称来将 CSP 的想法投入到并发编程实践中。

一个进程的输出应该直接流量另一个进程的输入。一个由守护的命令仅仅是一个带有左和右倾向的语句,由 → 来分隔。左侧服务是有运行条件的,或者是守护右侧服务,如果左侧服务运行失败,或者在一个命令执行后,返回 false 或者退出,右侧服务永远不会被执行。将这些与 Hoare 的思想组合机器,为 Hoare 通信过程奠定了基础,从而实现了 channel。

Go 如何帮助你

goroutine 把我们从必须按照并行的方式思考中解放出来,作为替代,它准许我们按照更为自然的等级对问题进行建模。

channel 帮助我们把每一个 goroutine 组合在一起。

select 语句是对 channel 的一个补充,并且是多个 channel 组合的所有难点得以实现。

这里我推荐刘丹冰的 GMP 讲解

Go 语言的并发哲学

“使用同行来共享内存,而不是是通过共享内存来通信“ ,这句座右铭想必每一个 Gopher 都熟知。在并发代码编写中 Go 提供了并发原语(go,channel) 和 sync 包供我们选择,那么我们什么时候应该选择并发原语,什么时候选择内存访问同步呢?以下这副图给出了答案:

你是否需要转让数据所有权?

如果需要将计算结果给你共享给其他的代码块,那么你应该选择 channel。这样做有两个好处:

  1. 创建一个带有缓存的 channel 来实现一个低成本的在内存中的队列来解耦你的生产者和消费者
  2. channel 确保你的并发代码可以和其他并发代码进行组合

你是否试图在保护某个数据结构的内部状态?

当你需要保护某一个数据结构的时候这是原子操作,临界区进入最小状态,我们需要将这个行为锁起来,所以这时使用 sync 包。

你是否试图协调多个逻辑片段?

记住,channel 比内存同步原语更具有可组合性。Go 团队鼓励漫天飞的 channel ,如果满篇锁对于任何语言可能都是灾难。

这是一个性能要求很高的临界区吗?

channel 使用做内存访问同步来操作,因此它只能更慢。

追求简洁,尽量使用 channel ,并且认为 goroutine 的使用是没有成本的。

2赞