写在前面
学习一个东西的一种很好的方法,就是去了解这个东西的历史。在我们学习 Go 的过程中,同样也可以去了解下在 Go 之前的一些事情。
内容
Rob Pike 是 Go 语言的作者之一,早年他在贝尔实验室工作,也是 Unix 团队的成员。
Go 里的很多思想,来源于他在之前开发过的一门用于关注输入设备交互的语言,叫 Squeak,后来还发展出了 Newsqueak.
(一个小插曲,正如前面所说,Squeak 是一门关注输入设备交互的语言,例如鼠标这些,而 Squeak 这个词,在英文里是吱吱叫的意思,就是老鼠的叫声)
所以我们有必要了解一下 Newsqueak 的一些设计。
Newsqueak
一般在并发问题上,程序员时常要关注线程、锁、信号量、共享内存等概念,但这从程序员进行简易编程的角度来说,还是难度比较高,层次还是比较低,我们需要一种属于高层次、屏蔽更多内容的东西。而 Newsqueak 就是用来解决并发问题,同时又希望能简单些的开发语言。
我们看一下 Newsqueak 的一些代码:
变量
hello: array of char = "hello";
i := 23 + 24;
b := mk(array[2] of array of char = {s , "go"});
在上面的变量定义和初始化的例子里,是不是就开始看到 Go 的雏形了?
函数
prog
是个关键字:
rec ack := prog(a , b: int) of int {
if (a == 0)
become b+1;
if (b == 0)
become ack(a-1, 1);
become ack(a-1, ack(a, b-1));
};
ack(3, 4) #调用它
在这里,become
可以简单理解为 return。
定义了函数后,我们可以把它当做一个值来使用:
sum := prog(a, b: int) of int {
become a + b;
}
difference := prog(a, b: int) of int {
become a - b;
}
sum = difference; #这里只是一个例子,但实际这样用肯定是很奇怪的
# 下面这两种结果是一样的
a := sum(24, 23);
a := prog(a, b: int) of int {
become a + b;
}(24, 23);
接下来,我们把函数当做一个程序来运行
begin sum(23, 5);
begin prog(a, b: int) {
for(; a >0; a = a-1)
print(b);
}(10, sum(23, 5)); # 这段代码会在后台进行打印
这里的 begin
就像 Go 里的 go
了。
channel
c : chan of int; # 声明
c = mk(chan of int); # 初始化
c <- = 23; # 发送
x = <- c; # 接收
Newsqueak 里认为这个通信过程是同步的:
- 发送者会被阻塞,直到有接收者
- 接收者会被阻塞,直到有发送者
- 如果两者都准备好了,那么值就会被传递
想要异步的话,可以这样:
begin prog(c: chan of int, a: int) {
c <-= a;
}(ch, v)
在 Newsqueak 里,channel 有以下几点:
- 它们应该被用来处理所有的通信问题
- 这样可以避免共享内存
- 将数据和信号做关联
- 它们应该像文件描述符一样,而不是文件,也就是我们拿到了这个句柄,就可以操作它做一些事情
- 它是 first-class values (不知道怎么翻译) ,大致意思就是可以像其它基本数据类型一样处理的值,也就是 channel 可以作为函数的参数、返回值、数据结构里的参数等。
与 channel 搭配的,还有一个叫 select 的东西
c1, c2: chan of int;
i: int;
select {
case i = <-c1:
print("A:", i);
case i = <-c2:
print("B:", i);
case i = <-c1:
print("C:", i);
case i = <-c2:
print("D:", i);
case c2 <-= 7:
print("S");
}
括号里的东西,会一直阻塞直到有一个 case 处理了内容。如果有多个 case 可以处理,就随机一个。
下面是一个 channel 使用的简单例子
counter := prog(c: chan of int) {
i := 1;
for (;;)
c <-= (i = i + 1);
};
c := mk(chan of int);
begin counter(c);
<-c;
2 #打印
<-c;
3 #打印
因为 Newsqueak 是用于处理交互设备的,我们看下在一个窗口系统里,客户端大概会使用 channel 来做怎样的定义:
type Env: struct of {
G: chan of graphics; #图形命令
M: chan of Mouse; #鼠标
K: chan of int; #键盘
};
type Client: prog(env: Env);
参考
Advanced Topics in Programming Languages: Concurrency/message passing Newsqueak