请看The examples from Tony Hoare's seminal 1978 paper中的这句话:
Go的设计深受Hoare论文的影响。虽然Go与本文中使用的示例语言有很大不同,但这些示例仍然很容易翻译。除了语法之外,最大的区别是Go将并发通信的管道明确地建模为通道,而Hoare的语言流程直接相互发送消息,类似于Erlang。 Hoare在第7.3节中暗示了这种可能性,但是有一个限制:"每个端口都连接到另一个进程中的另一个端口",在这种情况下,它将是一个主要的语法差异。
我很困惑。
Hoare语言中的流程直接相互通信。 Go例程也直接相互通信,但使用频道。
那么golang的局限性有什么影响。有什么真正的区别?
答案 0 :(得分:22)
答案需要更全面地了解Hoare在CSP上的工作。他的工作进展可归纳为三个阶段:
基于Dijkstra的信号量,Hoare开发了监视器。这些在Java中使用,除了Java的实现包含错误(参见Welch的文章Wot No Chickens)。不幸的是,Java忽视了Hoare后来的工作。
CSP从中脱颖而出。最初,CSP需要从流程A到流程B的直接交换。这种会合方法由Ada和Erlang使用。
CSP于1985年首次发布his Book时完成。最终版本的CSP包含Go中使用的频道。与牛津大学的Hoare团队一起,David May同时开发了 Occam ,这是一种故意将CSP融入实用编程语言的语言。 CSP和Occam相互影响(例如在The Laws of Occam Programming中)。多年来,Occam仅在Transputer处理器上提供,该处理器的架构适合CSP。最近,Occam has developed以其他处理器为目标,并且还吸收了Pi演算以及其他常规同步原语。
因此,回答最初的问题,将Go与CSP和Occam进行比较可能会有所帮助。
频道:CSP,Go和Occam对于频道都具有相同的语义。此外,Go可以轻松地将缓冲添加到频道中(Occam没有)。
选择:CSP定义内部和外部选项。但是,Go和Occam都有一种选择:在Go中选择,在Occam中选择 ALT 。事实证明,有两种CSP选择在实用语言中并不那么重要。
Occam的 ALT 允许条件防护,但是Go的选择没有(有一种解决方法:通道别名可以设置为 nil 模仿相同的行为。)
移动性:Go允许通过通道发送通道结束(以及其他数据)。这创建了一个动态变化的拓扑结构,超出了CSP的可能范围,但米尔纳的Pi演算是用他的CCS开发的,用来描述这样的网络。
进程:goroutine是一个分叉进程;它会在它想要时终止并且它没有父级。这不像CSP / Occam,其中流程是组合的。
这里有一个例子:首先是奥卡姆(n.b.缩进事项)
SEQ
PAR
processA()
processB()
processC()
其次是Go
go processA()
go processB()
processC()
在Occam案例中,processC在processA和processB终止之前不会启动。在Go中,processA和processB非常快速地分叉,然后processC直接运行。
共享数据:CSP并不直接关注数据。但值得注意的是,Go和Occam之间在共享数据方面存在重要差异。当多个goroutine共享一组共同的数据变量时,竞争条件是可能的; Go的优秀竞赛探测器有助于消除问题。但Occam采取了不同的立场:在编译时阻止共享的可变数据。
别名:与上述相关,Go允许多个指针引用每个数据项。 Occam不允许使用此类别名,因此减少了检测竞争条件所需的工作量。
后两点对于Hoare的CSP更少,更多关于May的Occam。但它们是相关的,因为它们直接涉及安全的并发编码。
答案 1 :(得分:5)
这正是关键点:在Hoare的初始论文(以及Erlang)中使用的示例语言中,进程A直接与进程B对话,而在Go中,goroutine A与通道C对话,goroutine B侦听通道C.I.e。在Go中,通道是显式的,而在Hoare的语言和Erlang中,它们是隐式的。
有关详细信息,请参阅this article。
答案 2 :(得分:0)
最近,我一直在与Go的频道进行大量的工作,并且多年来一直从事并发和并行性的工作,尽管我从未公开表示自己对此有所了解。
我认为您要问的是,向通道发送消息与直接发送消息之间的细微差别是什么?如果我了解您,那么快速的答案很简单。
发送到通道将为通道两侧的并行/并发提供机会。漂亮,可扩展。
我们生活在一个并发的世界中。从A到B连续发送一长串消息(异步)意味着B将需要以与A发送消息几乎相同的速度处理消息,除非B的多个实例有机会处理从渠道,从而分担了工作量。
关于通道的好处是,您可以具有许多生产者/接收者例程,它们可以将消息推送到队列,或从队列中消费并相应地对其进行处理。
如果您像单核CPU那样线性地考虑问题,那么并发基本上就像是要完成一百万个工作。知道单核CPU一次只能做一件事,但同时也看到这样的错觉:很多事情同时发生。执行某些代码时,操作系统需要等待一段时间才能使某些东西从网络,磁盘,键盘,鼠标等中恢复,甚至需要一些睡眠的过程,这使操作系统有机会执行其他操作同时。这一切发生得非常快,从而产生了并行性的幻觉。
另一方面,并行性是不同的,因为该作业可以在完全不同的CPU上运行,而不依赖于其他CPU的运行情况,因此不会在与其他CPU相同的约束下运行(尽管大多数OS都可以确保工作负载均匀分布以在所有CPU上运行的工作非常出色-也许除了CPU需求大,不合作的非OS生成代码,但即使如此,操作系统也会对其进行驯服。
关键是,拥有多核CPU意味着更多的并行性和并发性。
想象一下,在银行中,一个队列会扇出许多可以帮助您的出纳员。如果任何出纳员都没有为客户服务,那么一个出纳员会选择处理下一位客户并变得忙碌,直到他们都变得忙碌为止。每当客户离开柜员时,该柜员就能处理队列中的下一个客户。