概述
随着GO的流行,协程概念讨论越来越多,用一篇文章记录协程
协程
线程
在java中线程是一个重要概念,程序想要高性能高并发就离不开线程
- 在使用线程中一般会用线程池,即用有限的线程执行大量并发任务,以期达到减少线程数量,线程创建、关闭、切换的开销。
- 单单线程池还不足以支持高并发,在执行过程中有阻塞就会在一定程度上导致性能下降–>消除阻塞。
- 阻塞原因:sleep、wait、IO(read、write)
- 解决办法:io->NIO、sleep->回调(代码会被切分很多段,不符合人体工学难读->vertx)
协程
为了解决阻塞的问题,不让他阻塞,而是通过事件完成进行通知。即让线程切走执行其他任务,在完成任务后进行。执行sleep之前先注册一个事件,1s后唤醒接着上次的代码执行(java搞不了:要记住已经执行到了哪一行+保存上阶段所有变量,但是java中程序员无法控制线程切换)。
-
Go实现了一个调度器,现在调度器注册,调度器会到时候唤醒、同时保存了栈和变量即go自己实现了一个类似线程切换。唤醒后可以获得栈的内容,重新扔到线程池中,恢复内容->继续执行
- 相当于原来系统中有一个调度器,控制线程切换,但是这是内核的功能程序员没法控制,go完成了把内容保存到自己的栈上,在自己的线程之上又完成了一种调度,消除了阻塞,线程没有阻塞会接着执行别的事情。
- 自己实现栈切换—>实现二次调度---->消除阻塞
- 协程的目的就是为了消除阻塞。
-
协程实现方式:
- 有栈协程:Go Groutine
- 无栈协程:Rust
- 若有一个Task在执行的时候需要进行阻塞,基本思路也是从阻塞的位子切出,完成切回;需要有一个调度器,在Task本身记一个状态,在编译的时候阻塞的位置前后会被切分为两块,同时增加一个状态位,记录当前需要执行的代码块。在阻塞的时候记录位置,切到其他地方,完成之后切回来检查该执行哪块代码块,接着执行。(将任务编译为一个状态机)->切分为多个不阻塞的代码块->在多个代码块之间执行。
- 跳过阻塞代码块
总结
没有协程的性能不一定比协程低,只是通过回调不好读,不满足人体工学,协程要进行调度,代码实际多了,java技巧好了性能实际可以达到比go强,java代码复杂也可以达到很高并发,Go的高并发可以相当简单达到。
java可以实现但是需要技巧
Go可以实现且门槛低