时间:2022.03.10
环境:Windows10 Golang1.17
目的:Golang并发协程控制
说明:
作者:Zhong QQ交流群:121160124 欢迎加入!
目录
Channel特性
- 对一个已关闭的通道写入数据 ❌
- 关闭一个已关闭的通道 ❌
- 对一个已关闭的通道,读取数据 ✅
Channel关闭原则
- 不要在消费端(接收者)关闭channel,不要在有多个并行的生产者时对channel执行关闭操作。
- 当只有一个发送者时,无论有多少接收者,业务通道都应由唯一发送者关闭。
- 当有多个发送者,一个接收者时,应借助管理通道,由业务通道唯一接收者充当管理通道的发送者,其他业务通道的发送者充当接收者
- 当有多个发送者,多个接收者时,这是最复杂的,不仅要管理通道,还要另起一个专门的媒介协程,新增一个媒介通道,但核心逻辑都是一样。
- channel在没有任何goroutine引用的时候会自行关闭,而不需要显示进行关闭。 main程(主程)也是一个go程
- 推荐使用context管理go程 上面原则用context可以更简单达到目的
实例
1个发送者 1个接收者
/*
1个发送者 1个接收者
*/
package main
import (
"fmt"
"time"
)
func main() {
start_time := time.Now()
var ch = make(chan int)
// 发送者 自行管理channel
go func() {
for i := 0; i < 10; i++ {
ch <- i
}
close(ch)
}()
// 接受者
for v := range ch {
fmt.Println(v)
}
fmt.Printf("执行完成! 耗时:%v", time.Since(start_time))
}
context实现
package main
import (
"context"
"fmt"
"sync"
"time"
)
func main() {
start_time := time.Now()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var ch = make(chan int)
var wg sync.WaitGroup
wg.Add(1)
// 发送者 自行管理channel
go func() {
for i := 0; i < 10; i++ {
ch <- i
}
cancel()
}()
// 接受者
go func() {
defer wg.Done()
loop:
for {
select {
case <-ctx.Done():
break loop
//当ch关闭时有可能抢占读取到零值 未关闭时会阻塞所以上面的ctx会执行
case v := <-ch:
fmt.Println(v)
}
}
}()
wg.Wait()
fmt.Printf("执行完成! 耗时:%v", time.Since(start_time))
}
1个发送者 n个接收者
/*
1个发送者 n个接收者
*/
package main
import (
"fmt"
"math/rand"
"sync"
"time"
)
func main() {
start_time := time.Now()
rand.Seed(time.Now().UnixNano())
wg := sync.WaitGroup{}
wg.Add(100)
dataCh := make(chan int, 100)
// 发送者 主动关闭通道 因为接收者阻塞等待读取ch 所以不必关心此协程退出
go func() {
for i := 0; i < 200; i++ {
if i >= 100 {
close(dataCh)
break // or return
} else {
dataCh <- i
}
}
fmt.Println("ok")
}()
// 接收者
for i := 0; i < 100; i++ {
go func() {
defer wg.Done()
// 接收数据直到 dataCh 被关闭或者dataCh的数据缓存队列是空的。
for value := range dataCh {
fmt.Println(value)
}
}()
}
wg.Wait()
fmt.Printf("执行完成! 耗时:%v", time.Since(start_time))
}
context实现
/*
1个发送者 n个接收者 context实现
*/
package main
import (
"context"
"fmt"
"math/rand"
"sync"
"time"
)
func main() {
start_time := time.Now()
rand.Seed(time.Now().UnixNano())
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
wg := sync.WaitGroup{}
wg.Add(100)
dataCh := make(chan int, 100)
go func() {
for i := 0; i < 200; i++ {
if i >= 100 {
cancel()
break // or return
} else {
dataCh <- i
}
}
fmt.Println("ok")
}()
// 接收者
for i := 0; i < 100; i++ {
go func() {
defer wg.Done()
for {
select {
case <-ctx.Done():
return
case v := <-dataCh:
fmt.Println(v)
}
}
}()
}
wg.Wait()
fmt.Printf("执行完成! 耗时:%v", time.Since(start_time))
}
n个发送者 1个接收者
根据指定条件关闭信号channel 通知发送者停止发送并返回
/*
n个发送者 1个接收者
*/
package main
import (
"fmt"
"math/rand"
"sync"
"time"
)
func main() {
start_time := time.Now()
rand.Seed(time.Now().UnixNano())
const Max = 1000
const NumSenders = 100
wg := sync.WaitGroup{}
wg.Add(1)
dataCh := make(chan int) // 业务通道
stopCh := make(chan struct{}) // 管理通道:必须是无缓冲通道(即时退出) 其发送者是 业务通道的接收者。其接收者是 业务通道的发送者。
// 业务通道的发送者 也是管理通道的接收者
for i := 0; i < NumSenders; i++ {
go func() {
for {
// 提前检查管理通道是否关闭 让业务通道发送者早尽量退出
select {
case <- stopCh:
return
default:
}
select {
case <- stopCh:
return
case dataCh <- rand.Intn(Max):
}
}
}()
}
// 业务通道的接收者,亦充当管理通道的发送者
go func() {
defer wg.Done()
for value := range dataCh {
if value == Max-1 {
// 当达到某个条件时 通过关闭管理通道来广播给所有业务通道的发送者
close(stopCh)
fmt.Println(value)
return
}
fmt.Println("value: ", value)
}
}()
wg.Wait()
fmt.Printf("执行完成! 耗时:%v", time.Since(start_time))
}
主线程遍历channel 发送者go程完成发送后退出 等待go程执行等待和完成后的关闭channel任务
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
ch := make(chan interface{})
for i := 0; i < 5; i++ {
wg.Add(1)
i := i
go func() {
defer wg.Done()
ch <- []interface{}{i} // i不会重复
}()
}
go func() {
defer close(ch)
wg.Wait()
}()
for msg := range ch {
fmt.Println(msg) // 数据全面
}
}
context实现
/*
n个发送者 1个接收者 context实现
*/
package main
import (
"context"
"fmt"
"math/rand"
"sync"
"time"
)
func main() {
start_time := time.Now()
rand.Seed(time.Now().UnixNano())
const Max = 1000
const NumSenders = 100
wg := sync.WaitGroup{}
wg.Add(1)
dataCh := make(chan int) // 业务通道
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// 业务通道的发送者 也是管理通道的接收者
for i := 0; i < NumSenders; i++ {
go func() {
for {
// 提前检查管理通道是否关闭 让业务通道发送者早尽量退出
select {
case <- ctx.Done():
return
default:
}
select {
case <- ctx.Done():
return
case dataCh <- rand.Intn(Max):
}
}
}()
}
// 业务通道的接收者,亦充当管理通道的发送者
go func() {
defer wg.Done()
for value := range dataCh {
if value == Max-1 {
// 当达到某个条件时 通过关闭管理通道来广播给所有业务通道的发送者
cancel()
fmt.Println(value)
return
}
fmt.Println("value: ", value)
}
}()
wg.Wait()
fmt.Printf("执行完成! 耗时:%v", time.Since(start_time))
}
n个发送者 n个接收者
context实现
/*
n个发送者 n个接收者 context实现
*/
package main
import (
"context"
"fmt"
"math/rand"
"strconv"
"sync"
"time"
)
func main() {
rand.Seed(time.Now().UnixNano())
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
const Max = 100000
const NumReceivers = 10
const NumSenders = 1000
wg := sync.WaitGroup{}
wg.Add(NumReceivers)
// 1. 业务通道
dataCh := make(chan int)
var stoppedBy string
// 业务通道发送者
for i := 0; i < NumSenders; i++ {
go func(id string) {
for {
// 提前检查是否关闭
// 让业务通道发送者早尽量退出
select {
case <-ctx.Done():
return
default:
}
value := rand.Intn(Max)
select {
case <-ctx.Done():
return
case dataCh <- value:
}
}
}(strconv.Itoa(i))
}
// 业务通道的接收者
for i := 0; i < NumReceivers; i++ {
go func(id string) {
defer wg.Done()
for {
// 提前检查ctx是否cancel
// 让业务通道接收者早尽量退出
select {
case <-ctx.Done():
return
default:
}
select {
case <-ctx.Done():
return
case v := <-dataCh:
if v == 6666 {
stoppedBy = "接收者#" + id
cancel()
return
}
}
}
}(strconv.Itoa(i))
}
wg.Wait()
fmt.Println("被" + stoppedBy + "终止了")
}
关注微信公众号 加入qq技术聊天群 121160124