这是我参与「第三届青训营 -后端场」笔记创作活动的的第2篇笔记。
Go语言里的并发指的是能让某个函数独立于其他函数运行的能力。当一个函数创建为协程(goroutine)时,Go语言会将其视为一个独立的工作单元,这个单元会被调度到可用的逻辑处理器上执行。
并发与并行
理解操作系统的线程(thread)和进程(process),有助于理解Go语言运行时调度器如何得用操作系统来并发运行goroutine。
并行是让不同的代码片段同时在不同的物理处理器上执行。并行的关键是同时做很多事情,而并发是指同时管理很多事情,这些事情可能只做了一半就被暂停去做别的事情了。
使用较少的资源做更多的事情,是指导Go语言设计的哲学。
协程(goroutine)
操作系统自己掌管的进程(process)、进程内的线程(thread)以及进程内的协程(coroutine,也叫轻量级线程)。
协程的最大优势在于其“轻量级”,可以轻松创建上百万个协程而不会导致系统资源衰竭,而线程和进程通常最多也不能超过1万个。
Go语言在语言级别支持轻量级线程,叫goroutine。
协程基础
goroutine是Go语言并行设计的核心。
goroutine是通过Go程序的runtime管理的一个线程管理器。goroutine通过go关键字实现了,其实就是一个普通的函数。
goroutine 是 Go语言中的轻量级线程实现,由 Go 运行时(runtime)管理。Go 程序会智能地将 goroutine 中的任务合理地分配给每个 CPU。Go 程序从 main 包的 main() 函数开始,在程序启动时,Go 程序就会为 main() 函数创建一个默认的 goroutine。
Go语言中可以使用sync.WaitGroup来实现并发任务的同步。sync.WaitGroup内部维护着一个计数器,计数器的值可以增加和减少。例如当我们启动了N 个并发任务时,就将计数器值增加N。每个任务完成时通过调用Done()方法将计数器减1。通过调用Wait()来等待并发任务执行完,当计数器值为0时,表示所有并发任务已经完成。
package main
import (
"fmt"
"sync"
"time"
)
var wg sync.WaitGroup
func foo() {
defer wg.Done()
fmt.Println("foo")
time.Sleep(time.Second)
fmt.Println("foo end")
}
func bar() {
defer wg.Done()
fmt.Println("bar")
time.Sleep(time.Second*2)
fmt.Println("bar end")
}
func main() {
start:=time.Now()
wg.Add(2)
go foo()
go bar()
wg.Wait()
fmt.Println("程序结束,运行时间为",time.Now().Sub(start))
}