goroutine是go语言的并发体。在go语言里面能使用go关键字来实现并发。算法
go func()
goroutine本质上是协程,我刚刚学习的时候就粗略地认为goroutine是线程,直到最近才开始搞明白goroutine的基本概念。安全
在好久之前,人们但愿一个计算机(一个cpu)上能同时执行多项任务,让cpu在某段时间内进行分片,在某段很短期内执行程序a,而后又迅速得切换到程序b去执行,让人们看起来就像是两个程序在同时进行,这就是并发。并发
可是人们随之发现,cpu在切换程序的时候,若是不保存上一个程序的状态(也就是咱们常说的context--上下文),直接切换下一个程序,就会丢失上一个程序的一系列状态,因而引入了进程这个概念,用以划分好程序运行时所须要的资源。所以进程就是一个程序运行时候的所须要的基本资源单位(也能够说是程序运行的一个实体)。函数
若是一个电脑有多个cpu,每一个cpu都有进程在运行,这就是并行。学习
为了防止用户程序作出一些危险的指令,如关机,更改系统变量,修改别的进程数据,系统分为两种运行状态,用户态以及内核态,用户态是咱们的程序所在的状态,不能随便对内核的底层进行操做。若是咱们须要使用内核的底层操做的时候,内核提供了一种调用内核的接口,咱们调用这些接口也就是系统调用,在进行系统调用的时候,cpu会切换到内核态,才能执行内核的函数。线程
人们又发现一个问题,cpu切换多个进程的时候,会花费很多的时间,由于切换进程须要切换到内核态,而每次调度须要内核态都须要读取用户态的数据,进程一旦多起来,cpu调度会消耗一大堆资源,所以引入了线程的概念,线程自己几乎不占有资源,他们共享进程里的资源,内核调度起来不会那么像进程切换那么耗费资源。code
可是线程仍是须要内核去进行调度,切换起来也是须要把用户态的数据写入到内核态,也是须要耗费必定的计算机资源,那能够不能够将切换的调度改为咱们本身控制的呢,答案是有的,协程就是把本身的调度算法交给程序(用户态)去进行管理,能以更小的资源去进行并发。协程
goroutine就是一个协程例子,能够根据自身调度器进行调度,当某个gooutine调用了time.sleep方法或者channel,mutex阻塞时候,调度器会使其入睡,唤醒另外一个goroutine,根本不须要进入到内核态。接口
goroutine本质上是协程,能够理解为不受内核调度,而受go调度器管理的线程。goroutine之间能够经过channel进行通讯,以下:进程
func main() { c := make(chan string) go func(){ c <- "hello" }() go func(){ word := <- c + " world" fmt.Println(word) }() time.Sleep(1 * time.Second) }
goroutine只有在自身所在函数运行完毕,或者主函数运行完毕才会打断,于是上面的例子须要等待一秒,否则未执行完的goroutine会直接被打断。 若是咱们并发的线程数量多了以后,咱们不可能在main里面设置一个精确睡眠时间来评估全部的goroutine已经运行完毕而后退出。
这时候咱们可使用sync.WaitGroup来等待全部运行的goroutine运行结束后,再来退出main函数,主要原理是维护一个goroutine数量的计数器,每运行一个goroutine,计数器会加+1,运行结束后,计数器会-1,而后调用wait方法会一直阻塞,知道计数器为0,也就是当前运行的goroutine数量为0,实例以下:
func main() { var n sync.WaitGroup for i := 0; i < 20; i++ { n.Add(1) go func(i int, n *sync.WaitGroup) { defer n.Done() time.Sleep(1 * time.Second) fmt.Printf("goroutine %d is running\n", i) }(i, &n) } n.Wait() }