侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

go语言中的5种并发模式浅析及代码示例

Go  /  管理员 发布于 2年前   497

1、done-channel 模式

由于goroutine不会被垃圾回收,因此很可能导致内存泄漏,为了避免内存泄漏,goroutine 应该有被触发取消的机制。父Goroutine需要通过一个名为done的只读通道向其子Goroutine发送取消信号。

按照惯例,它被设置为第一个参数。

这种模式在其他模式中也被大量使用。

package main

import (
"fmt"
)

func main() {
jobs := make(chan int, 5)
done := make(chan bool)

go doWork(done, jobs)

for j := 1; j <= 3; j++ {

fmt.Println("sent job", j)
jobs <- j
}
close(jobs)
fmt.Println("sent all jobs")

// 任务结束
done <- true
}

func doWork(done chan bool, jobs chan int) {
for {
select {
case j, more := <-jobs:
if more {
fmt.Println("received job", j)
} else {
fmt.Println("received all jobs")
}
case <-done: // 任务结束,关闭子协程
return
default:
}
}
}


2、for- select 模式

这种模式通常用在从多个通道读取数据

package main

import (
"fmt"
"time"
)

func main() {
ch1, ch2 := make(chan int), make(chan int)

// 每2秒不断往通道1写数据
go func() {
i := 0
for {
i += 2
ch1 <- i
time.Sleep(2 * time.Second)
}
}()

// 每2秒不断往通道2写数据
go func() {
i := 1
for {
i += 2
ch2 <- i
time.Sleep(2 * time.Second)
}
}()

// 不断从通道读数据
for {
select {
case v := <-ch1:
fmt.Println("ch1:", v)
time.Sleep(time.Second)
case v := <-ch2:
fmt.Println("ch2:", v)
time.Sleep(time.Second)
default:
fmt.Println("default")
time.Sleep(time.Second)
}
}

}

如果 ch1 和 ch2 没数据,会走 default

如果 ch1 和 ch2 都有数据会随机选择一个执行,之所以随机是为了避免只执行第一个 case 导致饥饿


3、or-done 模式

该模式旨在将多个完成通道组合成一个 agg_done;

这意味着如果一个 done 通道发出信号,则整个agg_done通道也将关闭。然而,我们不知道在运行时完成通道的数量。

or-done 模式可以通过使用goroutine和递归来实现。

示例中使上下递归函数像树一样相互依赖,上部将自己的orDone通道注入下部,然后下层也将自己的orDone返回给上层,如果任何orDone通道关闭,则通知上层和下层。


这点和上面 done-channel 模式是不同的,上面是所有 goroutine 完成任务,这里是只要有 1 个 goroutine 完成就结束所有 goroutine。

就好比发送一个请求到多个微服务节点,只要有 1 个返回就算完成。

package main

import (
"fmt"
"time"
)

func main() {
var or func(channels ...<-chan interface{}) <-chan interface{}
// 只要有1个结束阻塞,关闭orDone并返回
or = func(channels ...<-chan interface{}) <-chan interface{} {
// 小于2个通道直接返回
switch len(channels) {
case 0:
return nil
case 1:
return channels[0]
}
// 声明一个orDone
orDone := make(chan interface{})
go func() {
// 完成关闭orDone
defer close(orDone)
switch len(channels) {
case 2: // 如果是2个channel,只需要监听这两个
select {
case <-channels[0]:
case <-channels[1]:
}
default:
// 二分法递归
m := len(channels) / 2
select {
case <-or(channels[:m]...):
case <-or(channels[m:]...):
}
}
}()
return orDone
}

// 传入一个时间模拟请求时长,时间到了就close掉,结束当前channel的阻塞
sig := func(after time.Duration) <-chan interface{} {
c := make(chan interface{})
go func() {
defer close(c)
time.Sleep(after)
}()
return c
}

start := time.Now()
// 这里orDone开始是阻塞的,里面开了5个channel
<-or(
sig(2*time.Hour),
sig(5*time.Minute),
sig(1*time.Second),
)
fmt.Printf("done after %v\n", time.Since(start))
}


4、fanout-channel 模式

意思是只有 1 个输入 channel,有多个输出 channel,经常用在设计模式中的观察者模式。

观察者模式中,当数据发生变动后,多个观察者都会收到这个信号。

package main

import (
"fmt"
"time"
)

func main() {
// 输入的channel,相当于被观察者
ch := make(chan interface{})
go func() {
for {
ch <- time.Now()
time.Sleep(3 * time.Second)
}
}()

// 观察者
out := make([]chan interface{}, 2)
for k := range out {
out[k] = make(chan interface{})
}

go fanout(ch, out)

// 是否观察到数据变化
for {
select {
case res := <-out[0]:
fmt.Println(res)
case res := <-out[1]:
fmt.Println(res)
}
}
}

func fanout(ch <-chan interface{}, out []chan interface{}) {
defer func() {
for i := 0; i < len(out); i++ {
close(out[i])
}
}()

// 订阅被观察者
for v := range ch {
v := v
for i := 0; i < len(out); i++ {
i := i
out[i] <- v
}
}

}


5、fan-in-channel 模式

和上面的相反,这个是指多个源 channel 输入,一个目标 channel 输出的情况。

package main

import (
"fmt"
"time"
)

func main() {
// 输入的channel
in := make([]chan interface{}, 2)
in2 := make([]<-chan interface{}, 2)

for k := range in {
k := k
in[k] = make(chan interface{})
var inin <-chan interface{} = in[k]
in2[k] = inin

go func() {
for {
in[k] <- time.Now()
time.Sleep(3 * time.Second)
}
}()

}

// 打印输出的channel
for v := range fanIn(in2...) {
fmt.Println(v)
}

}

func fanIn(chans ...<-chan interface{}) <-chan interface{} {
switch len(chans) {
case 0:
c := make(chan interface{})
close(c)
return c
case 1:
return chans[0]
case 2:
return mergeTwo(chans[0], chans[1])
default: // 多个channel二分法
m := len(chans) / 2
return mergeTwo(fanIn(chans[:m]...), fanIn(chans[m:]...))
}

}

func mergeTwo(a, b <-chan interface{}) <-chan interface{} {
// 针对2个channel输出
c := make(chan interface{})
go func() {
defer close(c)
for a != nil || b != nil {
select {
case v, ok := <-a:
if !ok {
a = nil
continue
}
c <- v
case v, ok := <-b:
if !ok {
b = nil
continue
}
c <- v
}

}
}()
return c
}

完


  • 上一条:
    国内网络“翻墙”必须要了解的知识
    下一条:
    centos7系统中xtrabackup数据备份工具的安装及使用
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • 在go中实现一个常用的先进先出的缓存淘汰算法示例代码(0个评论)
    • 在go+gin中使用"github.com/skip2/go-qrcode"实现url转二维码功能(0个评论)
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • 近期文章
    • 智能合约Solidity学习CryptoZombie二课:让你的僵尸猎食(0个评论)
    • 智能合约Solidity学习CryptoZombie第一课:生成一只你的僵尸(0个评论)
    • 在go中实现一个常用的先进先出的缓存淘汰算法示例代码(0个评论)
    • 在go+gin中使用"github.com/skip2/go-qrcode"实现url转二维码功能(0个评论)
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 欧盟关于强迫劳动的规定的官方举报渠道及官方举报网站(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • Laravel从Accel获得5700万美元A轮融资(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-10
    • 2017-09
    • 2020-03
    • 2020-05
    • 2020-06
    • 2020-07
    • 2020-12
    • 2021-01
    • 2021-05
    • 2021-06
    • 2021-07
    • 2021-08
    • 2021-10
    • 2021-11
    • 2021-12
    • 2022-01
    • 2022-02
    • 2022-03
    • 2022-04
    • 2022-05
    • 2022-06
    • 2022-07
    • 2022-08
    • 2022-09
    • 2022-10
    • 2022-11
    • 2022-12
    • 2023-01
    • 2023-02
    • 2023-03
    • 2023-04
    • 2023-05
    • 2023-06
    • 2023-07
    • 2023-08
    • 2023-09
    • 2023-10
    • 2023-11
    • 2023-12
    • 2024-01
    • 2024-02
    • 2024-03
    • 2024-04
    • 2024-05
    • 2024-06
    • 2024-07
    • 2024-08
    • 2024-11
    • 2025-02
    • 2025-04
    • 2025-05
    • 2025-06
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客