这是我参与「第五届青训营 」伴学笔记创作活动的第 14 天 Go为什么受欢迎,首要原因是快,Go语言不但通过有栈协程实现了轻量级的并发,而且还给了用户足够简单的使用方法,就是go这个关键字,也是go语言名称的由来。go的协程可以轻易的实现并发编程,它实际上是一个用户态的线程,可以简单认为他和系统真正的线程是一个n:m的关系。 除了快速的并发编程能力,Go语言的并发模型还有这些特别的玩法: 通道(Channel):Go语言中的通道是一种类型安全的、带锁的通信机制,可以在不同的协程之间安全地传递数据,保证数据的同步性和正确性。 select语句:可以通过select语句同时等待多个通道操作,从而避免了使用传统的互斥锁等同步机制带来的性能问题。 首先是最基础的并发编程,我们可以通过go关键字来创建一个协程,这个协程会和当前的协程并发执行,需要注意主协程结束后,所有的协程都会被强制结束,先暂时通过一些耗时操作等一等。
func main(){
go func(){
fmt.Println("hello")
}()
fmt.Println("world")
time.Sleep(time.Second)
}
这两个println是并行执行的。如果去掉time.Sleep,那么很可能只会打印出world,因为主协程结束后,所有的协程都会被强制结束。 为了避免使用sleep这种一点也不优雅的方式,我们可以使用sync.WaitGroup来等待所有的协程结束。
func main(){
var wg sync.WaitGroup
wg.Add(1)
go func(){
fmt.Println("hello")
wg.Done()
}()
fmt.Println("world")
wg.Wait()
}
这样就可以保证所有的协程都执行完毕后,主协程才会结束。而且既不会多等,也不会少等。 通道呢则是协调协程之间的通信的,我们可以通过make来创建一个通道,然后通过通道来传递数据。
func main(){
ch := make(chan int)
go func(){
ch <- 1
}()
fmt.Println(<-ch)
}
这个箭头自己就会阻塞,直到另一个协程把数据放进去,或者把数据取出来。 某种意义上来说,可以认为这是一个阻塞式消息队列。 select 提供了读取多个通道的能力,可以通过通道触发事件。
func main(){
ch := make(chan int)
go func(){
ch <- 1
}()
select {
case <-ch:
fmt.Println("hello")
}
}