参考:
https://draveness.me/golang/docs
goroutine and channel brief introduce
read the code below
package main
import (
"fmt"
"time"
)
// 求和,并将结果送入channel中
func sum(s []int, c chan int) {
sum := 0
for _, v := range s {
sum += v
}
fmt.Println(sum)
c <- sum // send sum to c
}
func main() {
s := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(s[:len(s)/2], c) //17
go sum(s[len(s)/2:], c) //-5
fmt.Println("this is main goroutine")
// sleep 1 sec, 主goroutine休眠1秒钟
time.Sleep(time.Duration(1)*time.Second)
fmt.Println("this is still main goroutine")
x, y := <-c, <-c // receive from c
fmt.Println("result is", x, y, x+y)
}
以上代码run出2种结果
result1:
this is main goroutine // 主goroutine
17
-5
this is still main goroutine // 主goroutine
result is 17 -5 12
result2:
this is main goroutine // 主goroutine
-5
17
this is still main goroutine // 主goroutine
result is -5 17 12
- 可以看到两个执行相同代码的goroutine并非先开始就先结束,它们是concurrent并发的,顺序不可预测
- go sum的2个goroutine,谁先打印sum谁就先向chan写入数据。由于chan不带缓存,是一个同步chan,而从chan取数据的main goroutine sleep 1 sec,因此chan缺少receiver。go sum的2个goroutine在 c <- sum 位置产生阻塞
Buffered Channels
Channels can be buffered. Provide the buffer length as the second argument to make to initialize a buffered channel
ch := make(chan int, 100)
package main
import "fmt"
func main() {
ch := make(chan int, 2) // buffer为2的chan
fmt.Println("send 1")
ch <- 1
fmt.Println("send 2")
ch <- 2
fmt.Println(<-ch)
fmt.Println(<-ch)
}
以上代码运行结果如下
send 1
send 2
1
2
当buffer未满时,向chan写数据永远不会阻塞,即使没有receiver;buffer满时,写入产生阻塞。通过缓存的使用,可以尽量避免阻塞,提供应用的性能
close channel
A sender can close a channel to indicate that no more values will be sent. Only the sender should close a channel, never the receiver. Closing is only necessary when the receiver must be told there are no more values coming, such as to terminate a range loop.
select
C语言中的 select 关键字可以同时监听多个文件描述符的可读或者可写的状态,Go语言中的 select 关键字也能够让 goroutine 同时监听多个 Channel 的可读或者可写,不可读或者不可写时,select 会一直阻塞当前的线程或者Goroutine
如果有同时多个channel可读/可写,那么Go会伪随机的选择一个channel并执行对应case后的操作(pseudo-random);否则select语句会阻塞,直到某个channel满足可读/可写
下面的代码展示了golang select的使用示例
package main
import (
"fmt"
)
func main() {
c := make(chan int, 10)
q := make(chan int, 10)
fibonacci(c, q)
}
func fibonacci(c, quit chan int) {
x, y := 0, 1
for {
select {
case c <- x:
ele := <-c
fmt.Println(ele, "to quit")
quit <- ele
x, y = y, x+y
case <-quit:
fmt.Println("quit")
return
}
}
}
上面的代码的输出有无数种可能
因为select结构会等待 c <- x 或者 <-quit 两个表达式中任意一个的返回。无论哪一个表达式返回都会立刻执行 case 中的代码,而当 select 中的两个 case 同时被触发时,会伪随机选择一个 case 执行
首次必然是case c <- x执行,其后则随机了
输出如
0 to quit
1 to quit
1 to quit
2 to quit
quit
0 to quit
quit
etc...
有疑问加站长微信联系(非本文作者)