并发 vs 并行

并发和并行虽然有一定的联系,但他们是两个不同的概念。并发指多个程序单元可以相互独立的运行;而并行是指多个程序单元可以同时运行。对于并发,关键词是“独立”。而并行则是与串行相对的概念。例如创建了多个线程或携程,他们都是相互独立运行的,所以是并发执行。如果他们同时在多个CPU上运行,则同时是并行执行。

例如,在超市排队结账时,每个人都是独立的个体,所以属于并发结账。如果有多个结账通道,则同时是并行结账。如果只有一个结账通道,则属于串行结账,每个独立的个体并发地分享这个结账通道。

从另一个角度来看,并发是并行的前提条件,只有先存在多个可以独立执行的程序单元,才有可能同时占用多个CPU执行。

go语言团队的成员Andrew Gerrand曾经做一个主题为《Concurrency is not parallelism》的讲座,其中对并发和并行有一句经典的总结:

In programming, concurrency is thecompositionof independently executing processes, while parallelism is the simultaneousexecutionof (possibly related) computations. Concurrency is aboutdealing withlots of things at once. Parallelism is aboutdoinglots of things at once.

注意上面的“processes”不是指进程,应该理解成独立运行的程序单元。

在go语言中,并发是通过goroutine实现的。只需在正常的函数调用之前加上关键词“go”即可。如下例所示,调用者不会等待DoSomething()返回,而是接着往下执行,因为会创建一个新的goroutine来执行函数DoSomething()。在go语言中,每个goroutine是独立运行的单元,所以goroutine是go语言并发执行的关键所在。

go DoSomething()

注意goroutine与thread(线程)不同,虽然他们都是可以独立执行的程序单元,但goroutine更轻量级,具有更快的启动速度,消耗更少的系统资源。多个goroutine可以绑定到同一个thread上执行。

在go语言中,程序员不能直接创建thread,只能创建goroutine。但程序员能通过runtime包中的下列函数设置可以同时执行的CPU的数量:

func GOMAXPROCS(n int) int

runtime包中同时提供了下面的函数,可以获取逻辑CPU的数量:

func NumCPU() int

所以以前经常能看到这样的代码:

runtime.GOMAXPROCS(runtime.NumCPU())

在新的go版本中,已经没有必要这样做了,因为每个进程可以同时执行的最大CPU的数量默认就是逻辑CPU的数量。

至少1.9.2之后的go版本是这样,因为我验证过1.9.2之后的版本。

细心的读者可能已经注意到go语言官网上对GOMAXPROCS的描述,这个接口将来可能会消失。既然go语言对并发已经做很好的封装和支持,完全没有理由还要程序员关心CPU数量这样细节问题,所以对于go语言将来要删除这个接口很容易理解。

GOMAXPROCS sets the maximum number of CPUs that can be executing simultaneously and returns the previous setting. If n

谈到并发,不得不提另一个概念:channel。channel为多个goroutine相互通信提供了go语言内置的支持,概念上与pipe(管道)类似。以前写过几篇go语言相关的文章,这里就不赘叙了。

所以,goroutine和channel是go语言中两个非常重要的概念,他们是实现并发的关键所在。

--END--

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180621G1YOMF00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券