根据2018年09月16日武汉·光谷猫友会,武汉的Gopher小伙伴分享的Go并发编程整理的内容。本次分享的主题内容包含Go语言并发哲学,并发的演化历史,你好并发,并发的内存模型,常见的并发模式等内容。关于并发编程的补充内容可以参考《Go语言高级编程》第一章的相关内容。

本次整理并发的演化历史部分的内容。

Go语言并发哲学

Do not communicate by sharing memory, instead, share memory by communicating!

不要通过共享内存来通信, 而是通过通信来共享内存!

不要逆行!

并发的演化历史

Go语言最早从UNIX系统的B语言和C语言演化而来,其中的并发特性是从Newsqueak、Alef和Limbo等语言演化而来。其中Newsqueak是Go之父Rob Pike于989年设计的语言,Alef则是Phil Winterbottom于1993年对C语言扩展了并发特性的语言,Limbo也是Rob Pike参与设计的支持并发的语言。由此可见,Rob Pike在并发编程语言领域已经积累了几十年的设计经验,Go语言正是站在这些前辈的基础上涅槃重生的。

Go语言并发的理论基础是来自Hoare于1978年发表的CSP论文(Hoare就是发明快速排序的大牛)。更通俗的类比,CSP对应的编程模型和UNIX中的管道非常相似,而管道更是在1964年就已经发明了。因此,从理论上看,Go语言的并发并非什么新发明的特性,它只不过是将CSP代表的通过消息同步的编程模型带入了工业开发领域。

Newsqueak素数筛 - Rob Pike, 1989

先看看素数筛的原理图:

然后是Newsqueak素数筛代码:

counter := prog(c:chan of int) {
    i:=2;
    for(;;)
        c <-= i++;
};

filter := prog(prime:int, listen, send:chan of int) {
    i:int;
    for(;;)
        if((i = <-listen)%prime)
            send <-= i;
};

sieve := prog() of chan of int {
    c := mk(chan of int);
    begin counter(c);
    prime := mk(chan of int);
    begin prog(){
        p:int;
        newc:chan of int;
        for(;;){
            prime <-= p =<- c;
            newc = mk();
            begin filter(p, c, newc);
            c = newc;
        }
    }();
    become prime;
};

prime:=sieve();

其中begin关键字启动一个并发,类似Go语言的go关键字。而become关键字表示返回值,类似return语句。因此说Newsqueak和Go语言的并发有很多相似之处。

Alef - Phil Winterbottom, 1993

然后是Alef语言。据说这个语言是C语言之父Ritchie所钟爱的语言。不过Alef只是短暂地出现在Plan9系统中。目前传世的官方文档只有入门指南和参考手册。

下面的代码是Alef文档中摘取的片段:

#include <alef.h>

void receive(chan(byte*) c) {
    byte *s;
    s = <- c;
    print("%s\n", s);
    terminate(nil);
}

void main(void) {
    chan(byte*) c;
    alloc c;
    proc receive(c);
    task receive(c);
    c <- = "hello proc or task";
    c <- = "hello proc or task";
    print("done\n");
    terminate(nil);
}

可以将Alef看作是类似C++的语言,基础的语法完善和C语言保存一致,但是在并发编程方向做了扩展。其中proc是启动一个进程,task是启动一个线程。

因为C语言没有GC特性,因此Alef并发所创建或分享的资源管理将会是一个极大的调整。并发的语法虽然看着很美,但是进行真正的并发编程可能没有那么容易。

Alef产生的并发体可能异常复杂,下图是Alef文档中摘取的图片:

总共有6个线程分布在3个进程中,现场并发体相互之间通过管道进行通信。因为Alef同时支持进程和线程,可以说它其实是伪装成编程语言的操作系统!

其它内容待续

在线浏览幻灯片:

https://talks.godoc.org/github.com/chai2010/awesome-go-zh/chai2010/chai2010-golang-concurrency.slide

幻灯片源文件:

https://github.com/chai2010/awesome-go-zh/tree/master/chai2010