0.1、索引

https://blog.waterflow.link/articles/1663551951058

1、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导致饥饿

2、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:
}
}
}

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
}

golang中的几种并发模式的更多相关文章

  1. python万能消费框架,新增7种中间件(或操作mq的包)和三种并发模式。

    新增的中间件和并发模式见注释. 消息队列中间件方面celery支持的,都要支持.并发模式,celery支持的都要支持. 从无限重复相似代码抽取框架,做成万能复用,是生产力的保障. 使用模板模式使加新中 ...

  2. go--->共享内存和通信两种并发模式原理探究

    共享内存和通信两种并发模式原理探究 并发理解 人类发明计算机编程的本质目的是为了什么呢?毫无疑问是为了解决人类社会中的各种负责业务场景问题.ok,有了这个出发点,那么想象一下,比如你既可以一心一意只做 ...

  3. Activity中的四种启动模式

    在Android中每个界面都是一个Activity,切换界面操作其实是多个不同Activity之间的实例化操作.在Android中Activity的启动模式决定了Activity的启动运行方式. An ...

  4. VMWare中的三种联网模式图解

    网络基础及局域网配置 1.简单的局域网结构 2.VMWare中的三种联网模式 NAT模式 桥接模式 VMnet1

  5. Http中的三种请求处理模式(MPM)的区别

    MPM---包括基于事件/异步,线程化和预分叉 MPM(multi-processing module)多种请求处理模式,分为三种工作模式: prefork worker event prefork- ...

  6. 详解 Java 中的三种代理模式

    代理模式 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能. 这里使用 ...

  7. [Swift实际操作]八、实用进阶-(2)Swift语言中的三种消息传递模式

    本文将通过响应按钮的点击事件,来演示Target-Action消息传递机制,该机制主要用于响应用户的界面操作.打开创建的空白项目.然后在左侧的项目导航区,打开视图控制器的代码文件:ViewContro ...

  8. this与JavaScrip中的四种调用模式

    this是什么 方法调用模式 构造器调用模式 函数调用模式 apply/call模式 this是什么 —In most languages, ‘this’ is a reference to the ...

  9. P2P网贷中的4种理财业务模式

     线上3种   直投标:线上理财人直接购买借款人的标,平台只是起个"撮合"作用,收点借款人的服务费.           借款人不还钱,有的平台会帮"借款人"还 ...

随机推荐

  1. 除了Synchronized关键字还有什么可以保证线程安全?

    除了Synchronized关键字还有什么可以保证线程安全?     日常使用Java开发时,多线程开发,一般就用Synchronized保证线程安全,防止并发出现的错误和异常,那么 除了Synchr ...

  2. 【unity游戏入门】2 使用代码编写Hello Unity游戏

    作者 罗芭Remoo 2021年9月24日 第一章.许可证的安装 下载好Unity之后,我们还需要一个前置操作才可以进入Unity引擎----许可证. 当然不用担心,Unity是一个开放的引擎,一切以 ...

  3. CF-1675D. Vertical Paths

    题意:每次可以选择一条路径,要求这条路径中每个点都是上一个点的子节点,求最少需要几条路径将所有点走完 思路:将每个点有没有子节点判断出来,因为只有没有子节点的点需要新增一条路,所以需要路径的最小数目就 ...

  4. spring项目中starter包的原理,以及自定义starter包的使用

    MAVEN项目中starter的原理 一.原始方式 我们最早配置spring应用的时候,必须要经历的步骤:1.pom文件中引入相关的jar包,包括spring,redis,jdbc等等 2.通过pro ...

  5. TextView 中文本内容换行

    TextView 中文本内容换行 首先如图所示,我的第一栏围城的书名和书的介绍是在同一行 但是我想让书名和书的介绍分开个站一行 这时我只要在我的数组文本中的文本用 "\n" 就可以 ...

  6. WinUI 3 踩坑记:从创建项目到发布

    本文是 WinUI 3 踩坑记 的一部分,该系列发布于 GitHub@Scighost/WinUI3Keng,若内容出现冲突以 GitHub 上的为准. 创建项目 现在 WinUI 3 的入门体验比刚 ...

  7. 微信小程序-前后端交互

    前台手机验证码登录 <view>手机号:</view> <input value="{{phone}}" bindinput="bindPh ...

  8. Chrome 浏览器缓存的问题

    浏览器缓存的问题 Chrome 在浏览器中设置: 1 Ctrl + Shift + Delete 2 Ctrl + F5 强制刷新 3 F12 打开控制台 ---> Network ---> ...

  9. service服务使用CoreDNS提供的域名地址访问

    普通的 Service:会生成 servicename.namespace.svc.cluster.local 的域名,会解析到 Service 对应的 ClusterIP 上,在 Pod 之间的调用 ...

  10. Kubernetes 安全

    RBAC 权限控制 对资源对象的操作都是通过 APIServer 进行的,那么集群是怎样知道我们的请求就是合法的请求呢?这个就需要了解 Kubernetes 中另外一个非常重要的知识点了:RBAC(基 ...