funcmain() { f() fmt.Println("Returned normally from f.") }
funcf() { deferfunc() { if r := recover(); r != nil { fmt.Println("Recovered in f", r) } }() fmt.Println("Calling g.") g(0) fmt.Println("Returned normally from g.") }
funcg(i int) { if i > 3 { fmt.Println("Panicking!") panic(fmt.Sprintf("%v", i)) } defer fmt.Println("Defer in g", i) fmt.Println("Printing in g", i) g(i + 1) }
//Calling g. //Printing in g 0 //Printing in g 1 //Printing in g 2 //Printing in g 3 //Panicking! //Defer in g 3 //Defer in g 2 //Defer in g 1 //Defer in g 0 //Recovered in f 4 //Returned normally from f.
并发
Go 中的并发可以通过轻量级线程的 Go routine 来实现。
Go routine
Go routine 是可以与另一个函数并行或并发的函数。
创建 Go routine 非常简单。 只需在函数前面添加关键字 Go,我们就可以使它并行执行。
Go routine 非常简单非常轻量级,因此我们可以创建数千个例程。
1 2 3 4 5 6 7 8 9 10 11 12
funcc() { time.Sleep(time.Second * 2) fmt.Println("I am concurrent") }
funcmain() { go c() fmt.Println("I am main") time.Sleep(time.Second * 2) } //=> I am main //=> I am concurrent
函数 c 是一个 Go routine,它与 Go 程序的主线程并行执行。
有时我们希望在多个线程之间共享资源。 Go 不是将一个线程的变量与另一个线程共享,因为这会增加死锁和资源等待的可能性。
还有另一种在 Go routine 之间共享资源的方法:通过 Go 语言的通道。
通道
通道在两个 Go routine 之间传递数据。 在创建通道时,必须指定通道接收的数据类型。
1 2 3 4 5 6 7 8 9 10 11
// 创建一个 string 类型的简单通道 c := make(chanstring)
// 有了这个通道,我们可以发送 string 类型数据。 我们都可以在此通道中发送和接收数据: funcmain(){ c := make(chanstring) gofunc(){ c <- "hello" }() msg := <-c fmt.Println(msg) } //=>"hello"
接收方通道将会一直等待发送方向通道发送数据。
单向通道
希望 Go routine 通过通道接收数据但不发送数据,反之亦然。 为此,我们还可以创建单向通道。
1 2 3 4 5 6 7 8 9
funcsc(ch chan<- string) { ch <- "hello" }
funcmain() { ch := make(chanstring) go sc(ch) fmt.Println(<-ch) }