• 《 Go 语言并发之道》读后感


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

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

    并发与并行的区别

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

    引用 Erlang 之父 Joe Armstrong 图

    erlang-之父并发与并行|600x451

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

    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 讲解

    https://zhuanlan.zhihu.com/p/168610624

    Go 语言的并发哲学

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

    选择-原语-内存同步|547x500

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

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

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

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

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

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

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

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

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

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

  • 相关阅读:
    lyt经典版MySQL基础——进阶6:连接查询-sql92语法-内连接
    lyt经典版MySQL基础——进阶4:常见函数-分组函数
    lyt经典版MySQL基础——进阶2:条件查询
    lyt经典版MySQL基础——进阶1:基础查询
    【转】jmeter如何设置登录接口只调用一次以及遇到的问题:cookie参数放在消息头headers里面
    Kafka命令行操作
    git上无法push代码解决办法
    【转】Jenkins集成Docker镜像实现自动发布
    springboot从一个Controller中的方法跳转到另一个Controller中的方法
    window.open
  • 原文地址:https://www.cnblogs.com/shangmo/p/14250019.html
Copyright © 2020-2023  润新知