介绍go中的协程, 通道, 通道缓冲, 通道同步和通道方向
Goroutines
goroutine 是一种轻量级的执行线程。
goroutines.go1 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
| package main
import ( "fmt" "time" )
func f(from string) { for i := 0; i < 3; i++ { fmt.Println(from, ":", i) } }
func main() { f("direct")
go f("goroutine")
go func(msg string) { fmt.Println(msg) }("going")
time.Sleep(time.Second) fmt.Println("done") }
|
当我们运行这个程序时,我们首先看到阻塞调用的输出,然后看到两个goroutine的输出。Goroutine的输出可以交错,因为Go运行时同时运行goroutine。
log1 2 3 4 5 6 7 8 9
| $ go run goroutines.go direct : 0 direct : 1 direct : 2 goroutine : 0 going goroutine : 1 goroutine : 2 done
|
接下来,我们将研究并发Go程序中goroutine的补充:通道。
Channels
通道是连接并发goroutine的管道。您可以将值从一个goroutine发送到通道,并将这些值接收到另一个Gooutine。
channels.go1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package main
import "fmt"
func main() { messages := make(chan string)
go func() { messages <- "ping" }()
msg := <-messages fmt.Println(msg) }
|
当我们运行程序时,“ping”消息通过我们的通道成功地从一个goroutine传递到另一个。
log1 2
| $ go run channels.go ping
|
默认情况下,发送和接收块,直到发送方和接收方都准备就绪。该属性允许我们在程序结束时等待“ping”消息,而不必使用任何其他同步。
Channel Buffering
默认情况下,通道是无缓冲的,这意味着如果有相应的接收(<-chan)准备接收发送的值,它们将仅接受发送(chan<-)。缓冲通道接受有限数量的值,而没有相应的接收器来接收这些值。
channel-buffering.go1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package main
import "fmt"
func main() { messages := make(chan string, 2)
messages <- "buffered" messages <- "channel"
fmt.Println(<-messages) fmt.Println(<-messages) }
|
log1 2 3
| $ go run channel-buffering.go buffered channel
|
Channel Synchronization
我们可以使用通道在goroutine之间同步执行。下面是一个使用阻塞接收来等待goroutine完成的示例。当等待多个goroutine完成时,您可能更喜欢使用WaitGroup。
channel-synchronization.go1 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 main
import ( "fmt" "time" )
func worker(done chan bool) { fmt.Print("working...") time.Sleep(time.Second) fmt.Println("done")
done <- true }
func main() { done := make(chan bool, 1) go worker(done)
<-done }
|
log1 2
| $ go run channel-synchronization.go working...done
|
如果删除该程序中的 <- done 行,程序会在 Worker 开始之前退出。
Channel Directions
在使用通道作为函数参数时,可以指定通道是只用于发送还是接收数值。这种特殊性提高了程序的类型安全性。
channel-directions.go1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package main
import "fmt"
func ping(pings chan<- string, msg string) { pings <- msg }
func pong(pings <-chan string, pongs chan<- string) { msg := <-pings pongs <- msg }
func main() { pings := make(chan string, 1) pongs := make(chan string, 1) ping(pings, "passed message") pong(pings, pongs) fmt.Println(<-pongs) }
|
log1 2
| $ go run channel-directions.go passed message
|
参考链接