golang之所有好用,是因为其将以前编程中常用的而且晦涩难用的异步任务以及信号机制,以傻瓜式的语法给封装了一层。接触了golang已经有一段时间了,现在回头再看一下golang的协程机制,顺便做个总结。
一. 协程机制
系统内核可以理解成一个经验丰富的管家,能把以前无系统下的各种任务(包括各种异步任务同步任务)给处理的很得当。其基本思想就是建造了进程对象,让cpu在多进程下来回切换,却又让进程无法察觉。这种解决方案彻底将应用层编程给分离了出来。但是后来随着任务量的增加,人们发现频繁的切换进程会造成很多额外的消耗(内存表等进程资源的切换消耗),而随着任务量增加,这种问题尤为突出。于是人们又发明了线程(Thread),线程是基于进程下的多任务,多线程降低了因为系统的进程量,多线程可以共用进程资源,但是多线程的任务切换也离不开内核的上下文切换以及cpu的寄存器内容切换。
golang基于以上计算机成果,在线程上创造了异步任务的实现方法---协程
协程组成:
1. M(应用层调度器):进行协程间的任务调度
2. P(系统线程):决定了最大并发执行任务数,通过GOMAXPROCS参数控制线程数
3. G(协程):独立运行的基本单位
基本思想:调度器执行时会创建线程池,并把协程放到线程上执行,待协程到了触发点(协程退出,使用channel、系统调用等休眠)时,调度器会把原有协程取出,再把待执行的协程放到线程里面执行。可以看到,这是在应用层实现了内核的任务切换功能;
资源消耗对比:
线程:2M栈,需要内核参与调度
协程:2K栈,不需要内核cpu进行任务调度
因此,可以说golang的协程非常轻量
二. 异常处理
1. golang没有java和php的try catch机制。其提议通过多返回值的形式抛出错误;
2. 但同时又支持通过defer,recover来处理panic错误(若不捕捉怎进程异常退出),如:
package main
import(
"fmt"
"time"
)
func test(){
defer func(){ // 必须要先声明defer,否则不能捕获到panic异常
if err:=recover();err!=nil{
fmt.Println(err) // 这里的err其实就是panic传入的内容,55
}
}()
panic("panic")
}
func main(){
go test()
for{
fmt.Println("1")
time.Sleep(time.Duration(1)*time.Second)
}
}
输出:
1
panic
1
1