GO 语言并发模型
哲学
不要通过共享内存来通信, 应该通过通信来共享内存
进程、线程、协程
进程
- 操作系统中最核心的概念是进程。
- 进程是“程序执行的一个实例” ,担当分配系统资源的实体。
- 进程创建必须分配一个完整的独立地址空间。
- 让计算机能够同时处理多个任务,操作系统有了进程的概念, 而且在进程内部,基本可以认为当前系统只有一个进程在运行, 操作系统对此作了非常好的封装。进程间的切换是有操作系统来完成的。
线程
线程是进程的一个执行流,独立执行它自己的程序代码。 线程是操作系统能够进行运算调度的最小单位。
线程的问题是:
1)存在线程安全问题,出了问题非常不易定位。
2)进程内部有线程数目的限制。
3)随着并发量的增加,线程生成和切换的成本也变得昂贵。
协程
- 协程不同于线程的地方在于协程不是操作系统进行切换, 而是由程序员编码进行切换的,也就是说切换是由程序员控制的, 这样就没有了线程所谓的安全问题。
- 所有的协程都共享整个进程的上下文,这样协程间的交换也非 常方便。
缺点:协程的缺点可能是无法利用多核优势。
简而言之:编程语言提供的一种并发机制,不受操作系统的调度,由 程序员来控制。
并发编程难度
并发编程的难度在于协调,而协调就要通过通信。
并发通信模型: 共享数据和消息。
基于内存的消息通信缺点:
我们还能用多线程干活,只是因为我们手里还有加锁机制,
而它可以部分地堵上线程模型的漏洞。讽刺的是,
引入加锁机制解决问题的同时,又带来了新的问题,
所以我们编写多线程程序总会遇上死锁,活锁,优先级反转……等等。
基于消息共享内存的优点:
- 抹去共享变量。(解决资源竞争问题)
- 异步传递消息。
- 可控制。
个人理解: 基于内存的消息通信最大的缺点就是程序员不可控制,它是由 操作系统控制的,而基于消息的并发模型是程序员可以控制的。
go语言并发编程
goroutine 是协程的实现,由Go runtime管理,使用只需要
go 关键字开启。
channel 是goroutine间的传递消息的通道。(类似Unix中的管道)
通道是协程之间的数据传输通道。通道可以在众多的协程之间传递数据,
具体可以值也可以是个引用。通道有两种使用方式。
· 协程可以试图向通道放入数据,如果通道满了,会挂起协程,
直到通道可以为他放入数据为止。
· 协程可以试图向通道索取数据,如果通道没有数据,会挂起协程,
直到通道返回数据为止。
使用
package main import "fmt" func Add(ch chan int, x, y int) { z := x + y // 往channel中写数据 ch <- z } func main() { // 定义10个channel chs := make([]chan int, 10) for i := 0; i < 10; i++ { chs[i] = make(chan int) // 使用goroutine go Add(chs[i], i, 1) } for i, ch := range chs { fmt.Println("chan index", i) // 读channel中的数据 value := <-ch fmt.Println("value", value) } }
总结
- Golang的并发模型是基于消息来共享内存,这种模型的好处就是程序员可以控制并发。