改变策略,先说答案再说分析:
怎么就适合并发了?
确实是这样的,但是原因可能和你认为的不太一样,大家都知道go关键字可以快速开启协程也就是轻量级线程,携程也拥有自己的栈空间,且go为我们提供了可伸缩等一系列内存优化策略,还提供了channel作为goroutine通信的基础,但是这就适合并发场景了吗?
其实不然,设想一个场景,我们在java并发编程中,使用onthread-onconnect的经典io多路复用模型时不足在什么地方,答案是线程的上下文切换,设想我们一个线程触发了io中断时因为java的线程模型是一对一的混合线程模型,所以会触发系统级io中断陷入内核态,如果多个connect同时出发那么系统上下文切换的开销是巨大的,这也就是java的nio模型的不足之处(当然可以通过reactor模型去优化,这里是举反例说明)。
说回到go。go的并发编程优势就体现在这里,根据GPM模型分析当我们一个goroutine触发了中断G就会被park,当前线程会被中断,但是我们后续的goroutine不会受到影响,因为GPM会把其他goroutine切换mechine去执行,那么提现到操作上就是仿佛go就给我们实现了非阻塞机制,假设我们自己去实现一套事件驱动框架呢?当然性能只会更加可控更加高效。
但是这里有个特殊情况那就是网络IO,golang底层实际上实现了netpoller底层封装了epoll/kqueue实现事件驱动机制,所以仅仅会park住G而不会使M被中断。
最后渗透一点GPM的概念
这里引用一张图片
其实这个图片已经很清晰了,当然延续上文,我们在某个线程中断挂起后实际上是会产生任务窃取的,也就是说 会触发摘除创建新的线程或加入空闲的M继续服务,加上每个goroutine的时间片是非常短的期间空闲线程会发生窃取,所以几乎可以做到阻塞无感。当然goroutine实际上实现的是一个协程,可以简单理解为一个用户级线程,调度器在切换goroutine的时候并不会陷入内核态,所以自然要高效很多。