本文主要介绍goroutine 和channel的使用。
基础知识介绍
goroutine 是 golang 中在语言级别实现的轻量级线程,仅仅利用 go 就能立刻起一个新线程。多线程会引入线程之间的同步问题,在 golang 中可以使用 channel 作为同步的工具。
通过 channel 可以实现两个 goroutine 之间的通信。
创建一个 channel, make(chan TYPE {, NUM}) TYPE 指的是 channel 中传输的数据类型,第二个参数是可选的,指的是 channel 的容量大小。
向 channel 传入数据, CHAN <- DATA , CHAN 指的是目的 channel 即收集数据的一方, DATA 则是要传的数据。
从 channel 读取数据, DATA := <-CHAN ,和向 channel 传入数据相反,在数据输送箭头的右侧的是 channel,形象地展现了数据从隧道流出到变量里。
举三个例子
package main
/**
使用通道,类似于多线程计算
*/
import (
"fmt"
"math/rand"
"time"
)
func sum(s []int, c chan int) {
sum := 0
for _, v := range s {
time.Sleep(time.Duration(rand.Int31n(10)) * time.Millisecond)
sum += v
}
c <- sum // 把 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)
}
/**
通道缓冲区, 关闭通道
*/
package 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)
// range 函数遍历每个从通道接收到的数据,因为 c 在发送完 10 个
// 数据之后就关闭了通道,所以这里我们 range 函数在接收到 10 个数据
// 之后就结束了。如果上面的 c 通道不关闭,那么 range 函数就不
// 会结束,从而在接收第 11 个数据的时候就阻塞了。
for i := range c {
fmt.Println(i)
}
fmt.Print("finish")
}
package main
import (
"fmt"
"time"
)
func say(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s, (i+1)*100)
}
}
func say2(s string, ch chan int) {
for i := 0; i < 5; i++ {
time.Sleep(150 * time.Millisecond)
fmt.Println(s, (i+1)*150)
}
ch <- 0
close(ch)//关闭通道并不会丢失里面的数据,只是让读取通道数据的时候不会读完之后一直阻塞等待新数据写入
}
func main() {
ch := make(chan int)
go say2("world", ch)
say("hello")
fmt.Println(<-ch)//主函数会等待信道的值才会结束。
}
//注释掉的方法,在执行中会发现,say2方法并没有执行完成。这是因为goroutine 还没来得及跑完 5 次的时候,主函数已经退出了。通过引入信道,可以解决这个问题。
/**
package main
import (
"fmt"
"time"
)
func say(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s, (i+1)*100)
}
}
func say2(s string) {
for i := 0; i < 5; i++ {
time.Sleep(150 * time.Millisecond)
fmt.Println(s, (i+1)*150)
}
}
func main() {
go say2("world")
say("hello")
}
*/
上面三个例子都是关于多线程和线程间通信的例子。
Channel 控制读写权限
go func(c chan int) { //读写均可的channel c } (a)
go func(c <- chan int) { //只读的Channel } (a)
go func(c chan <- int) { //只写的Channel } (a)
网友评论