说明
本章主要说明Go语言中的线程相关
下面的示例代码来自于这里
线程
创建
- goroutine 是由 Go 运行时环境管理的轻量级线程,使用go关键字来开启一个新的 goroutine 执行
go f(x, y, z)
- 下面是一个具体使用线程的示例
package main
import (
"log"
"time"
)
func say(s string) {
for i := 0; i < 5; i++ {
time.Sleep(1000 * time.Millisecond)
log.Println(s)
}
}
func main() {
go say("hello")
say("world")
}
管道
创建
- channel 是有类型的管道,可以用 channel 操作符 <- 对其发送或者接收值
ch <- v // 将 v 送入 channel ch。
v := <-ch // 从 ch 接收,并且赋值给 v。
- 创建一个channel对象可以使用如下方法
ch := make(chan int)
缓冲区
- channel可以设置缓冲区
package main
import "fmt"
func main() {
c := make(chan int, 1)//缓冲区大小为1
c <- 1
fmt.Println(<-c)
c <- 2
fmt.Println(<-c)
}
关闭
- 可以调用close方法来关闭一个 channel,close方法应与发送者位于同一线程中
close(c)
- 接收者可以通过赋值语句的第二参数来判断channel 是否被关闭:当没有值可以接收并且 channel 已经被关闭
v, ok := <-ch
此时 ok 的值为false
分支
分支选择
select
语句可以使一个 goroutine 在多个通讯操作上等待
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)
}
- 其中 case 的条件必须是取值、赋值、或者取值的断言
默认选择
- 当 select 中的其他条件分支都没有准备好的时候,
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)
}
}
}
坑
默认情况下,在另一端准备好之前,发送和接收都会阻塞。这使得 goroutine 可以在没有明确的锁或静态变量的情况下进行同步
只有发送者才能关闭 channel,而不是接收者。向一个已经关闭的 channel 发送数据会引起 panic
channel 与文件不同;通常情况下无需关闭它们。只有在需要告诉接收者没有更多的数据的时候才有必要进行关闭,例如中断一个
range
package main
import (
"fmt"
)
func fibonacci( c chan int) {
x, y := 0, 1
for i := 0; i < cap(c); i++ {
c <- x
x, y = y, x+y
}
close(c)
}
func main() {
c := make(chan int, 10)
go fibonacci( c)
for i := range c {
fmt.Println(i)
}
fmt.Println(i, ok)//ok为false
}