go以高并发著称,而支撑这个高并发的,goroutine协程是主要原因之一。
goroutine让我们轻松实现异步,非阻塞。
但这种异步,也带来的问题是执行顺序的不确定性。
以及数据同步的问题。在这种情况下,go官方引入了channel来通信,实现数据共享。
channel是用于goroutine的数据通信
在这张图上面,我们在主协程,总共开了5个goroutine,然后每个goroutine,通过ch的channel通信共享内存,处理写以及读的数据。
Channel 通过操作符 <- 执行 写入以及读取操作。
ch <- v // 写入v到channel
v := <-ch // 从channel读取数据。
(箭头的指向就是数据的流向)
通道有发送(send)、接收(receive)和关闭(close)三种操作。
我们先定义一个channel,因为channel的空值是nil,所以必须make才能使用。
ch := make(chan int)
将一个值发送到通道中。
ch <- 10 //把10发送到 ch 中
从一个通道中接收值。
x := <-ch //从ch中接收值,并赋值给x
<-ch //从ch中接收值,忽略结果
我们通过调用内置的close函数来关闭通道。
close(ch)
//main.关闭一个 nil 值 channel 会引发 panic。!
func TestNew(t *testing.T) {
var ch chan struct{}
close(ch)
}
//关闭一个已关闭的 channel 会引发 panic
func TestClose(t *testing.T) {
ch := make(chan struct{})
close(ch)
close(ch)
}
//向一个已关闭的 channel 发送数据。会引发 panic。
func TestCloseSend(t *testing.T) {
ch := make(chan struct{})
close(ch)
ch <- struct{}{}
}
我们读取一个chanel,这个channel一直到有写入的时候,才能读取,否则会一直阻塞着。
func TestSync(t *testing.T) {
done := make(chan bool)
go worker(done)
//一直阻塞着,等待任务完成
<-done
}
func worker(done chan bool) {
time.Sleep(time.Second*10)
// 通知任务已完成
done <- true
}
这样也有一个问题,万一worker这个协程,有很长时间的话,就会一直阻塞,我们可以加一个保底的时间。
func TestSync(t *testing.T) {
done := make(chan bool)
go worker(done)
//一直阻塞着,等待任务完成
select {
case <-done:
case <-time.After(time.Second):
}
}
func worker(done chan bool) {
time.Sleep(time.Second * 10)
// 通知任务已完成
done <- true
}
我们用select改进了一下,让它有一个1秒的打底。超过1秒,就可以走出来了,不用一直阻塞着。
如下面的代码,
开了三个channel(http1,http2,time.After),两个子协程。
用select来监听,哪个channel先返回。
import (
"math/rand"
"testing"
"time"
)
func TestSelect(t *testing.T) {
res1 := make(chan string)
res2 := make(chan string)
go http1(res1)
go http2(res2)
select {
case v1 := <-res1:
t.Log(v1)
case v2 := <-res2:
t.Log(v2)
case <-time.After(650 * time.Millisecond):
t.Log("650 Millisecond over, timeover")
}
time.Sleep(time.Second * 1)
}
func http1(res1 chan string) {
time.Sleep(500 * time.Millisecond)
res1 <- "res1 ok"
}
func http2(res3 chan string) {
//[0-1000)Millisecond 随机
rand.Seed(time.Now().UnixNano())
time.Sleep(time.Millisecond * time.Duration(rand.Intn(1000)))
res3 <- "res3 ok"
}
输出:
res2 ok
或者
res1 ok
看http2随机的事多少。