Go 并发编程
- Go 程
- 信道
- 带缓冲的信道
- range 和 close
Go 程
Go 程(goroutine)是由 Go 运行时管理的轻量级线程。
1
2
3
4
5
6go f(x, y, z) 会启动一个新的 Go 程并执行 f(x, y, z) f, x, y 和 z 的求值发生在当前的 Go 程中,而 f 的执行发生在新的 Go 程中。
Go 程在相同的地址空间中运行,因此在访问共享的内存时必须进行同步。sync 包提供了这种能力,不过在 Go 中并不经常用到,因为还有其它的办法(见下一页)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20package 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") }
1
2
3
4
5
6
7
8
9
10hello world world hello hello world world hello hello
信道
信道是带有类型的管道,你可以通过它用信道操作符 <- 来发送或者接收值。
1
2
3
4ch <- v // 将 v 发送至信道 ch。 v := <-ch // 从 ch 接收值并赋予 v。 (“箭头”就是数据流的方向。)
和映射与切片一样,信道在使用前必须创建:
1
2ch := make(chan int)
默认情况下,发送和接收操作在另一端准备好之前都会阻塞。这使得 Go 程可以在没有显式的锁或竞态变量的情况下进行同步。
以下示例对切片中的数进行求和,将任务分配给两个 Go 程。一旦两个 Go 程完成了它们的计算,它就能算出最终的结果。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22package main import "fmt" func sum(s []int, c chan int) { sum := 0 for _, v := range s { sum += v } c <- sum // 将和送入 c } func main() { s := []int{7, 2, 8, -9, 4, 0} c := make(chan int) go sum(s[:len(s)/2], c) go sum(s[len(s)/2:], c) x, y := <-c, <-c // 从 c 中接收 fmt.Println(x, y, x+y)
1
2-5 17 12
带缓冲的信道
信道可以是 带缓冲的。将缓冲长度作为第二个参数提供给 make 来初始化一个带缓冲的信道:
1
2ch := make(chan int, 100)
仅当信道的缓冲区填满后,向其发送数据时才会阻塞。当缓冲区为空时,接受方会阻塞。
1
2
3
4
5
6
7
8
9
10
11
12
13package main import "fmt" func main() { ch := make(chan int, 2) ch <- 1 ch <- 2 fmt.Println(<-ch) fmt.Println(<-ch) }
正常输出
1
2
31 2
修改为1
1
2
3
4
5
6fatal error: all goroutines are asleep - deadlock! goroutine 1 [chan send]: main.main() /tmp/sandbox3447111331/prog.go:8 +0x4b
将fmt.Println(<-ch) 改为fmt.Println(ch),输出地址值
1
2
3
40xc00005a070 1
range 和 close
发送者可通过 close 关闭一个信道来表示没有需要发送的值了。接收者可以通过为接收表达式分配第二个参数来测试信道是否被关闭:若没有值可以接收且信道已被关闭,那么在执行完
1
2
3v, ok := <-ch 之后 ok 会被设置为 false。
循环 for i := range c 会不断从信道接收值,直到它被关闭。
注意: 只有发送者才能关闭信道,而接收者不能。向一个已经关闭的信道发送数据会引发程序恐慌(panic)。
还要注意: 信道与文件不同,通常情况下无需关闭它们。只有在必须告诉接收者不再有需要发送的值时才有必要关闭,例如终止一个 range 循环。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24package main import ( "fmt" ) func fibonacci(n int, c chan int) { x, y := 0, 1 for i := 0; i < n; i++ { c <- x x, y = y, x+y } close(c) } func main() { c := make(chan int, 10) go fibonacci(cap(c), c) for i := range c { fmt.Println(i) } }
1
2
3
4
5
6
7
8
9
10
11
120 1 1 2 3 5 8 13 21 34
最后
以上就是昏睡哑铃最近收集整理的关于【Go tour】并发章节Go 程信道带缓冲的信道range 和 close的全部内容,更多相关【Go内容请搜索靠谱客的其他文章。
发表评论 取消回复