协程(goroutine) 协程是go语言内部唯一实现程序并发的方式,go不支持创建系统线程
在Go中,开启一个新的协程是非常简单的。 我们只需在一个函数调用之前使用一个go关键字,即可让此函数调用运行在一个新的协程之中。 当此函数调用退出后,这个新的协程也随之结束了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package mainimport ( "log" "math/rand" "time" ) func SayGreetings (greeting string , times int ) { for i := 0 ; i < times; i++ { log.Println(greeting) d := time.Second * time.Duration(rand.Intn(5 )) / 2 time.Sleep(d) } } func main () { rand.Seed(time.Now().UnixNano()) log.SetFlags(0 ) go SayGreetings("hi!" , 10 ) go SayGreetings("Hello!" , 10 ) time.Sleep(2 * time.Second) }
并发同步 使用通道技术可以实现并发同步,sync标准库提供的WaitGroup可以很简单实现这一点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 package mainimport ( "log" "math/rand" "runtime" "sync" "time" ) var wg sync.WaitGroupfunc SayGreetings (greeting string , times int ) { for i := 0 ; i < times; i++ { log.Println(greeting) d := time.Second * time.Duration(rand.Intn(5 )) / 2 time.Sleep(d) } wg.Done() } func main () { rand.Seed(time.Now().UnixNano()) log.SetFlags(0 ) log.Println(runtime.NumCPU()) wg.Add(2 ) go SayGreetings("hi v!" , 10 ) go SayGreetings("hello z!" , 10 ) wg.Wait() time.Sleep(2 * time.Second) log.Println("end" ) }
延迟函数 一个在defer后面的函数调用就是延迟函数,多个延迟函数遵守后进先出规则执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package mainimport "fmt" func main () { defer fmt.Println("9" ) fmt.Println("0" ) defer fmt.Println("8" ) fmt.Println("1" ) if false { defer fmt.Println("not reachable" ) } defer func () { defer fmt.Println("7" ) fmt.Println("3" ) defer func () { fmt.Println("5" ) fmt.Println("6" ) }() fmt.Println("4" ) }() fmt.Println("2" ) return defer fmt.Println("not reachable" ) }
修改函数返回值 一个延迟调用可以修改包含此延迟调用的最内层函数的返回值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package mainimport "fmt" func Triple (n int ) (r int ) { defer func () { r += n }() return n + n } func main () { fmt.Println(Triple(5 )) }
协程和延迟调用的实参的估值时刻 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package mainimport "fmt" func main () { func () { for i := 0 ; i < 3 ; i++ { defer fmt.Println("a:" , i) } }() fmt.Println() func () { for i := 0 ; i < 3 ; i++ { defer func () { fmt.Println("b:" , i) }() } }() }
可以对第二个循环修改一下,使得它的结果和第一个一样
1 2 3 4 5 6 for i := 0 ; i < 3 ; i++ { defer func (i int ) { fmt.Println("b:" , i) }(i) }
同样的估值时刻规则也适用于协程调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package mainimport "fmt" import "time" func main () { var a = 123 go func (x int ) { time.Sleep(time.Second) fmt.Println(x, a) }(a) a = 789 time.Sleep(2 * time.Second) }
恐慌和恢复 Go不支持异常抛出和捕获,而是推荐使用返回值显式返回错误。 不过,Go支持一套和异常抛出/捕获类似的机制。此机制称为恐慌/恢复(panic/recover)机制。
1 2 3 4 5 6 7 8 9 10 11 12 13 package mainimport "fmt" func main () { defer func () { recover () fmt.Println("panic is over" ) }() panic ("panic is coming" ) fmt.Println("end?" ) }