PS: 作为服务端,event loop最核心的就是I/O多路复用技术,所有来自客户端的请求都由I/O多路复用函数来处理;作为客户端,event loop的核心在于Future对象延迟执行,并使用send函数激发协程,挂起,等待服务端处理完成返回后再调用Callback函数继续执行。[python 协程与go协程的区别]
3.1 Golang 协程Go 天生在语言层面支持,和python类似都是用关键字,而GO语言使用了go关键字,go协程之间的通信,采用了channel关键字。
go实现了两种并发形式:
多线程共享内存:如Java 或者C++在多线程中共享数据的时候,通过锁来访问
Go语言特有的,也是Go语言推荐的 CSP(communicating sequential processes)并发模型。
package main import ("fmt") func main() { jobs := make(chan int) done := make(chan bool) // end flag go func() { for { j, ok := <- jobs fmt.Println("---->:", j, ok) if ok { fmt.Println("received job") } else { fmt.Println("end received jobs") done <- true return } } }() go func() { for j:= 1; j <= 3; j++ { jobs <-j fmt.Println("sent job", j) } close(jobs) fmt.Println("close(jobs)") }() fmt.Println("sent all jobs") <-done // 阻塞 让main等待协程完成 }Go的CSP并发模型是通过goroutine 和 channel来实现的。
goroutine是go语言中并发的执行单位。
channel是Go语言中各个并发结构体之间的通信机制。
channel -< data 写数据
<- channel 读数据
协程本质上来说是一种用户态的线程,不需要系统来执行抢占式调度,而是在语言测个面实现线程的调度。
4. 并发并发:Do not communicate by sharing memory; instead, share memory by communicate.
4.1 Actor模型Actor模型和CSP模型的区别:
CSP并不Focus发送消息的实体/Task, 而是关注发送消息时消息所使用的载体,即channel。
在Actor的设计中,Actor与信箱是耦合的,而在CSP中channel是作为first-class独立存在的
Actor中有明确的send/receive关系,而channel中并不区分这样的关系,执行快可以任意选择发送或者取消息
好文推荐:Go/Python/Erlang编程语言对比分析及示例
4.4 Go 协程调度器 GPMG 指的是Goroutine,其本质上也是一种轻量级的线程
P proessor, 代表M所需要的上下文环境,也是处理用户级代码逻辑处理器。同一时间只有一个线程(M)可以拥有P, P中的数据都是锁自由(lock free)的, 读写这些数据的效率会非常的高
M Machine,一个M直接关联一个内核线程,可以运行go代码 即goroutine, M运行go代码需要一个P, 另外就是运行原生代码,如 syscall。运行原生代码不需要P。
一个M会对应一个内核线程,一个M也会连接一个上下文P,一个上下文P相当于一个“处理器”,一个上下文连接一个或者多个Goroutine。P(Processor)的数量是在启动时被设置为环境变量GOMAXPROCS的值,或者通过运行时调用函数runtime.GOMAXPROCS()进行设置
erlang和golang都是采用CSP模型,python中协程是eventloop模型。但是erlang是基于进程的消息通信,go是基于goroutine和channel通信。
python和golang都引入了消息调度系统模型,来避免锁的影响和进程线程的开销问题。
计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决 -- G-P-M模型正是此理论践行者,此理论也用到了python的asyncio对地狱回调的处理上(使用Task+Future避免回调嵌套),是不是巧合?
其实异步≈可中断的函数+事件循环+回调,go和python都把嵌套结构转换成列表结构有点像算法中的递归转迭代.
调度器在计算机中是分配工作时所需要的资源,Linux的调度是CPU找到可运行的线程,Go的调度是为M线程找到P(内存,执行票据)和可运行的G(协程)