Concurrency
Goroutines
go
package main
import (
"fmt"
"time"
)
func say(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func main() {
go say("world")
say("hello")
}
Channels
Channel = 管道,使用 <-
來將值傳入管道或由管道中接收值。
ch <- v // Send v to channel ch.
v := <-ch // Receive from ch, and
// assign value to v.
跟 maps 與 slices 相同,需要使用 make 來宣告:
ch := make(chan int)
channel 必須指定型別
package main
import "fmt"
func sum(a []int, c chan int) {
sum := 0
for _, v := range a {
sum += v
}
c <- sum // send sum to c
}
func main() {
a := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(a[:len(a)/2], c)
go sum(a[len(a)/2:], c)
x, y := <-c, <-c // receive from c
fmt.Println(x, y, x+y)
}
Buffered Channels
Channel 是可以有緩衝的。
ch := make(chan int, 100)
當 channel 滿的時候將值傳入 channel 將會被阻塞,而當 channel 為空時接收值也會被阻塞。
package main
import (
"fmt"
"time"
)
func put(x int, c chan int) {
c <- x
}
func putLater(x int, c chan int) {
time.Sleep(2 * time.Second)
put(x, c)
}
func main() {
ch := make(chan int, 2)
ch <- 1
ch <- 2
go put(3, ch)
go putLater(4, ch)
fmt.Println(<-ch)
fmt.Println(<-ch)
fmt.Println(<-ch)
fmt.Println(<-ch)
}
Range and Close
若 channel 已經沒有值需要傳入時可以透過 close
來關閉 channel,也可以用接收時的第二個回傳值來判斷 channel 是否已經關閉
v, ok := <-ch
當 ok
為 false
時,代表沒有值可以接收而且 channel 已經被關閉。
使用 for i := range c
可以一直從 channel 接收值,直到 channel 被關閉。
註1: 只有發送者應該去關閉 channel,不要由接收方來關閉。傳入值到一個已經關閉的 channel 會導致 panic。
註2: Channel 不像檔案需要再使用後關閉,channel 通常不需要被關閉,只有需要告訴接收方已經沒有值需要接收時才需要關閉,像是結束一個 range
迴圈。
package main
import (
"fmt"
)
func main() {
c := make(chan int, 10)
c <- 1
close(c)
value, ok := <-c
fmt.Println(value, ok)
value, ok = <-c
fmt.Println(value, ok)
channelForRange := make(chan int, 10)
channelForRange <- 1
channelForRange <- 2
close(channelForRange)
for i := range channelForRange {
fmt.Println(i)
}
}
Select
Select
可以用來等待多個 channel 的回傳。
Select
會阻塞住,直到任一個 case 可以執行。當多個 case 都可執行時它會隨機選擇一個 case 執行。
package main
import "fmt"
func fibonacci(c, quit chan int) {
x, y := 0, 1
for {
select {
case c <- x:
x, y = y, x+y
case <-quit:
fmt.Println("quit")
return
}
}
}
func main() {
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
}()
fibonacci(c, quit)
}
Default Selection
跟 switch
一樣,若有 default
時 select
將在沒有任何 case 可執行時來執行 default
。
package main
import (
"fmt"
"time"
)
func main() {
tick := time.Tick(100 * time.Millisecond)
boom := time.After(500 * time.Millisecond)
for {
select {
case <-tick:
fmt.Println("tick.")
case <-boom:
fmt.Println("BOOM!")
return
default:
fmt.Println(" .")
time.Sleep(50 * time.Millisecond)
}
}
}