并发
Go是并发式语言,而不是并行式语言。
并发是什么?
并发是指立即处理多个任务的能力。举个例子:假如一个人在跑步,他的鞋带突然松了。于是他停下来,系一下鞋带,接下来继续跑。这就可理解为并发,这个人能够一下搞定跑步和系鞋带两件事,即立即处理多个任务。
并行是什么?
并行是指同时处理多个任务。举个例子:假如一个人在跑步时,还在用iPod听着音乐。在这里,他是在跑步的同时听音乐,也就是同时处理多个任务,这称之为并行。
从技术上看并发和并行
假如我们正在编写一个web浏览器。这个web浏览器有各种组件,其中两个分别是web页面的渲染区和从网上下载文件的下载器。假设我们已经构建好了浏览器代码,各个组件也都可以相互独立地运行。
当浏览器在单核处理器中运行时,处理器会在浏览器的两个组件间进行上下文切换。它可能在一段时间内下载,转而又对用户请求的web页面进行渲染。这就是并发。并发的进程从不同的时间点开始,分别交替运行。在这里,就是在不同的时间点开始进行下载和渲染,并相互交替运行的。
如果该浏览器在一个多核处理器上运行,此时下载文件的组件和渲染HTML的组件可能会在不同的核上同时运行。这称之为并行。
注意:并行不一定会加快运行速度,因为并行运行的组件之间可能需要相互通信。在浏览器的例子里,当文件下载完成后,应当对用户进行提醒,比如弹出一个窗口。于是,在负责下载的组件和负责渲染用户界面的组件之间,就产生了通信。在并发系统上,这种通信开销很小。但在多核的并行系统上,组件间的通信开销就很高了。所以,并行不一定会加快运行速度!
Go对并发的支持
Go语言原声支持并发。Go使用Go协程(goroutine)和信道(channel)来处理并发。
Go协程
Go协程是与其它函数或方法一起并发运行的函数或方法。Go协程可以看作是轻量级线程。与线程相比,创建一个Go协程的成本很小。
Go协程相比于线程的优势
- 相比线程而言,Go协程的成本极低。堆栈大小只有若干KB,并且可以根据应用的需求进行增减。而线程必须指定堆栈的大小,其堆栈是固定不变的。
- Go协程会复用(Multiplex)数量更少的OS线程。即使程序有数以千计的Go协程,也可能只有一个线程。如果该线程中的某一Go协程发生了阻塞(比如说等待用户输入),那么系统会再创建一个OS线程,并把其余Go协程都移动到这个新的OS线程。所有这一切都在运行时进行。
- Go协程使用Channel来进行通信。Channel用于防止多个协程访问共享内存时发生竞态条件(Race Condition)。Channel可以看作是Go协程之间通信的管道。
启动一个Go协程
调用函数或者方法时,在前面加上关键字go
,就可以让一个新的Go协程并发地运行。
但需要理解两个Go协程的主要性质:
- 启动一个新的协程时,协程的调用会立即返回。与函数不同,程序控制不会去等待Go协程执行完毕。在调用Go协程之后,程序控制会立即返回到代码的下一行,忽略该协程的任何返回值。
- 如果希望运行其它Go协程,Go主协程必须继续运行着。如果Go主协程终止,则程序终止。于是其它Go协程也不会继续运行。
reference:
https://studygolang.com/articles/12341
https://studygolang.com/articles/12342