go并发学习笔记
包含了go学习笔记,含有channel的基础学习,编写数字的平方,如何成组的合并channel,如何优雅的关闭退出并发协程,通道阻塞情况分析,channel与哪些变成情况,可谓是收藏好文.
文章目录
- 并发1:channel的基础学习
- 并发2:编写数字的平方
- 并发3:组装式编写数字的平方
- 并发4:如何关闭退出并发协程
- 并发5:通道阻塞情况
- 并发5:channel情况汇总
并发1:channel的基础学习
func main() {
ch := make(chan int, 2)
ch <- 1
ch <- 2
elem, ok := <-ch
if ok != true {
fmt.Println("channel closed")
}
fmt.Println(elem)
}
一个从channel里读,一个从channel里写
func main() {
syncChan1 := make(chan struct{}, 1)
syncChan2 := make(chan struct{}, 2)
go func() {
<-syncChan1
fmt.Println("receive a sync signal and wait a second...[receiver]")
time.Sleep(time.Second)
for {
if elem, ok := <-strChan; ok {
fmt.Println("Received:", elem, "[receiver]")
} else {
break
}
}
fmt.Println("stopped [receiver]")
syncChan2 <- struct{}{}
}()
go func() {
for _, elem := range []string{"a", "b", "c", "d"} {
strChan <- elem
fmt.Println("sent:", elem, "[sender]")
if elem == "c" {
syncChan1 <- struct{}{} //唤醒接收的go携程,
fmt.Println("sent a sync signal:", elem, "[sender]")
}
}
fmt.Println("ait 2 seconds...[sender]")
time.Sleep(time.Second * 2)
close(strChan)
syncChan2 <- struct{}{}
}()
<-syncChan2
<-syncChan2
}
赋值写入
type Counter struct {
count int
}
var mapChan2 = make(chan map[string]*Counter, 1)
func (counter *Counter) String() string {
return fmt.Sprintf("counter:%d", counter.count)
}
func main() {
syncChan := make(chan struct{}, 2)
go func() {
for {
if elem, ok := <-mapChan2; ok {
counter := elem["count"]
counter.count++
} else {
break
}
}
fmt.Println("stopped [receiver]")
syncChan <- struct{}{}
}()
go func() {
countMap := map[string]*Counter{
"count": &Counter{},
}
for i := 0; i < 5; i++ {
mapChan2 <- countMap
time.Sleep(time.Millisecond)
fmt.Printf("The count map::%v [sender]\n", countMap)
}
close(mapChan2)
syncChan <- struct{}{}
}()
<-syncChan
<-syncChan
}
用struct进行通信
func main() {
dataChan := make(chan int, 5)
syncChan1 := make(chan struct{}, 1)
syncChan2 := make(chan struct{}, 2)
go func() {
<-syncChan1
for {
if elem, ok := <-dataChan; ok {
fmt.Printf("Received:%d [receiver]\n]", elem)
} else {
break
}
}
fmt.Println("Done [receiver]")
syncChan2 <- struct{}{}
}()
go func() {
for i := 0; i < 5; i++ {
dataChan <- i
fmt.Printf("Sent:%d [sender]\n", i)
}
close(dataChan)
syncChan1 <- struct{}{}
syncChan2 <- struct{}{}
}()
<-syncChan2
<-syncChan2
}
用select case进行读取
func main() {
chanCap := 5
intChan := make(chan int, chanCap)
for i := 0; i < chanCap; i++ {
select {
case intChan <- 1:
case intChan <- 2:
case intChan <- 3:
}
}
for i := 0; i < chanCap; i++ {
fmt.Println(<-intChan)
}
}
并发2:编写数字的平方
func producer(nums ...int) <-chan int {
out := make(chan int)
go func() {
defer close(out)
for _, n := range nums {
out <- n
}
}()
return out
}
func square(inCh <-chan int) <-chan int {
out := make(chan int)
go func() {
defer close(out)
for n := range inCh {
out <- n * n
}
}()
return out
}
func main() {
in := producer(1, 2, 3, 4, 5, 6)
out := square(in)
for ret := range out {
fmt.Println(ret)
}
fmt.Println("done")
}
并发3:组装式编写数字的平方
func producer(nums ...int) <-chan int {
out := make(chan int)
go func() {
defer close(out)
for _, n := range nums {
out <- n
}
}()
return out
}
func square(inCh <-chan int) <-chan int {
out := make(chan int)
go func() {
defer close(out)
for n := range inCh {
out <- n * n
}
}()
return out
}
func merge(cs ...<-chan int) <-chan int {
out := make(chan int)
var wg sync.WaitGroup
collect := func(in <-chan int) {
defer wg.Done()
for n := range in {
out <- n
}
}
wg.Add(len(cs))
for _, c := range cs {
go collect(c)
}
go func() {
wg.Wait()
close(out)
}()
return out
}
func main() {
in := producer(1, 2, 3, 4, 5, 6)
c1 := square(in)
c2 := square(in)
c3 := square(in)
for ret := range merge(c1, c2, c3) {
fmt.Printf("%3d ", ret)
}
fmt.Println("\ndone")
}
并发4:如何关闭退出并发协程
文章链接:退出
- 使用for-range退出
- 使用,ok退出
- 使用退出通道退出
并发5:通道阻塞情况
// 场景1
// 通道中无数据,但执行读通道
func ReadNoDataFromNoBufCh() {
noBufCh := make(chan int)
<-noBufCh //通道中没数据,你还读,必然阻塞
fmt.Println("read from no buffer channel success")
}
//场景2
//通道中无数据,向通道中写数据,但无协程读取
func WriteNoBufCh() {
ch := make(chan int)
ch <- 1 //通道中无数据,向通道写数据,但无协程读取
fmt.Println("write to no buffer channel success")
}
//场景3
//通道的缓存中无数据但执行读通道
func ReaNoDataFromBufCh() {
noBufCh := make(chan int, 1)
<-noBufCh //通道中没数据,你还读,必然阻塞
fmt.Println("ReaNoDataFromBufCh")
}
//场景4
//通道缓存已经占满,向通道写数据,但无协程读
func WriteBufChButFull() {
ch := make(chan int, 1)
ch <- 1
ch <- 2 //通道缓存已经占满,向通道写数据,但无协程读
fmt.Println("WriteBufChButFull")
}
并发5:channel情况汇总
场景 | 原理 | 用法 |
---|---|---|
需要不断从channel读取数据时 | 该方法,当channel关闭时,for循环自动退出,可以防止读取已经关闭的channel | for x := range ch {} |
v,ok := <- ch + select 操作判断channel是否关闭 | ok为true,读到数据,ok为false,没读到数据 | v,ok := <- ch + select |
需要对多个通道进行同时处理,但只处理最先发生的channel时 | select 可以同时监视多个通道的情况,只处理未阻塞的case,当通道为nil时,对应的case永远为阻塞,无论读写,特殊关注:普通情况下,对nil的通道写操作是要panic的 | select |
如果协程只读或只写 | 双向通道变成单向通道 | 单向通道只能只读或只写 |
异步 | 有缓冲通道可供多个协程同时处理,在一定程度可提高并发性 | make(chan int,10) |
需要超时控制的操作 | 使用select 和time.After ,看操作和定时哪个先返回,处理先完成的,就达到了超时控制的效果 | case <- time.After() |
并不希望在channel的读写上浪费时间 | 是为操作加上超时的扩展,这里的操是channel的读或写 | case <-time.After() |
退出时,显示通道所有协程退出 | 所有读ch协程都会收到close(ch)的信号 | 使用close(ch)关闭所有下游协程 |
使用channel传递信号,而不是传递数据时 | 没数据需要传递时,传递空struct | 使用chan struct{} 作为信号channel |
使用channel传递结构体数据时 | channel本质上传递的是数据的拷贝,拷贝的数据越小传输效率越高,传递结构体指针,比传递结构体更高效 | 使用channel传递结构体的指针而非结构体 |
用来获取结果 | channel可以用来传递变量,channel自身也是变量,可以传递自己 | 使用channel传递channel |