你能用几种方式实现?知道为啥吗?
我们知道go语言最大的特性是:原生支持并发;同时流行一句话:不通过共享内存通讯,通过通讯来共享内存。
这就引出一等公民:channel goroutine
话外音:简单回忆下理论
- 进程(process)、线程(thread)、协程(goroutine/coroutine)之间关系如图(网上找的)
编辑
传统并发编程:多个进程/线程要对临界资源加锁等,通过判断临界资源状态来实现并发。
channl: 通道,多个goroutine可通过channel来通讯。(思考:goroutine通过他实现在通讯中共享内存,可参考GMP模型后面会写)
channel是什么?
简单提下:通过var或make在函数栈上声明8byte的指针,指向堆的hchan的结构体。
有几个核心字段
buf:环形数组(有缓冲的channel)
sendx: 下一个要发送数据的下标
recvx: 下一个要接收数据的下标
sendq: 写队列(发送时,读队列没有goroutine接收,发送的goroutine存在这里)
recvq: 读队列 (接收时, 写队列没有gonroutie发送,接收的goroutine存在这里)
closed: 是否被关闭(关闭可接受零值, 发送panic)
理解了上面:自然也知道为啥有缓channel:buf满-发送阻塞,buf空-接收阻塞
话外音:有无缓冲channel就这点区别。
醒醒,别吹牛X啦,跑题了不就并发打印数字么。。。
话不多说上代码:
type1:
ch := make(chan int)
for i := 0; i < 10; i++ {
go func(i int) {
ch <- i
}(i)
}
for i := 0; i < 10; i++ {
print(<-ch)
}
type2:
func main() {
ch := make(chan int)
for i := 0; i < 10; i++ {
go func(i int) {
ch <- i
}(i)
print(<-ch)
}
}
这个留个坑,咋避免顺序打印呢(不用type1)
type3:
type worker struct {
ch chan int
done func()
}
func main() {
quit := make(chan struct{})
w := worker{
ch: make(chan int),
done: func() {
quit <- struct{}{}
},
}
for i := 0; i < 10; i++ {
go func() {
for n := range w.ch {
print(n)
w.done()
}
}()
}
for i := 0; i < 10; i++ {
w.ch <- i
}
for i := 0; i < 10; i++ {
<-quit
}
}
type4:
type worker struct {
ch chan int
done func()
}
func main() {
wg := sync.WaitGroup{}
w := worker{
ch: make(chan int),
done: func() {
wg.Done()
},
}
for i := 0; i < 10; i++ {
go func() {
for n := range w.ch {
print(n)
w.done()
}
}()
}
wg.Add(10)
for i := 0; i < 10; i++ {
w.ch <- i
}
wg.Wait()
}
话外音:还有很多种写法就,后面在补上。
你瞅瞅理解后不是随便写。。。
专注是最好的学习方法,但不是靠毅力(理性脑有远见却很弱小)。给自己正向反馈,负向更是礼物。共勉。