go语言并发模型

GO 语言并发模型

哲学

不要通过共享内存来通信, 应该通过通信来共享内存

进程、线程、协程

进程

  1. 操作系统中最核心的概念是进程。
  2. 进程是“程序执行的一个实例” ,担当分配系统资源的实体。
  3. 进程创建必须分配一个完整的独立地址空间。
  4. 让计算机能够同时处理多个任务,操作系统有了进程的概念, 而且在进程内部,基本可以认为当前系统只有一个进程在运行, 操作系统对此作了非常好的封装。进程间的切换是有操作系统来完成的。

线程

线程是进程的一个执行流,独立执行它自己的程序代码。 线程是操作系统能够进行运算调度的最小单位。

线程的问题是:
1)存在线程安全问题,出了问题非常不易定位。
2)进程内部有线程数目的限制。
3)随着并发量的增加,线程生成和切换的成本也变得昂贵。

协程

  1. 协程不同于线程的地方在于协程不是操作系统进行切换, 而是由程序员编码进行切换的,也就是说切换是由程序员控制的, 这样就没有了线程所谓的安全问题。
  2. 所有的协程都共享整个进程的上下文,这样协程间的交换也非 常方便。

缺点:协程的缺点可能是无法利用多核优势。

简而言之:编程语言提供的一种并发机制,不受操作系统的调度,由 程序员来控制。

并发编程难度

并发编程的难度在于协调,而协调就要通过通信。
并发通信模型: 共享数据和消息。

基于内存的消息通信缺点:
我们还能用多线程干活,只是因为我们手里还有加锁机制,
而它可以部分地堵上线程模型的漏洞。讽刺的是,
引入加锁机制解决问题的同时,又带来了新的问题,
所以我们编写多线程程序总会遇上死锁,活锁,优先级反转……等等。

基于消息共享内存的优点:

  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的并发模型是基于消息来共享内存,这种模型的好处就是程序员可以控制并发。