当前位置: 首页 > news >正文

go并发之美·多个channel合并/多个数据流合并

多个数据流(来自于不同channel)合并为一个流。

一般用于多个相同性质来源的数据进行合并为一处进行统一处理。

 

目录

背景

实现·赖着不走

变个花样:学成出师


 

背景

最近在重温武侠剧,无意间想到了一些情形然后手痒,想顺手来模拟一下,接着有了此文。

平时可能经常碰到这样的场景,如一个业务逻辑可能会统筹多个/多份不同来源的同类型数据,然后该业务逻辑进行统筹汇总并统一处理汇总后的结果。

以武侠剧为例,杨过受到了来自多位大师的指点学到了多方武功,然后自己自行吸收处理,对于杨过来说,可以学一段停下来自行吸收作罢;也可持续学习持续吸收(变化无穷,乐趣无穷)。

很有意思,一起来体验下。

实现·赖着不走

上代码

// 入口(可能就是拜师流程)
func main() {c1()
}

持续教授武功&持续学习武功

func c1() {process := make(chan struct{})ch1, ch2, ch3 := make(chan string), make(chan string), make(chan string)go func() { // 黄药师的指点for i := 1; ; i++ {data := fmt.Sprintf("a%s", strconv.Itoa(i))fmt.Println("黄药师 教学: ", data)ch1 <- datatime.Sleep(time.Second * 2) // sleep纯属为观察,可不加}}()go func() { // 洪七公的教学for i := 1; ; i++ {data := fmt.Sprintf("b%s", strconv.Itoa(i))fmt.Println("洪七公 教学: ", data)ch2 <- datatime.Sleep(time.Second * 2)}}()go func() { // 欧阳锋的教学for i := 1; ; i++ {data := fmt.Sprintf("c%s", strconv.Itoa(i))fmt.Println("欧阳锋 教学: ", data)ch3 <- datatime.Sleep(time.Second * 2)}}()result := foo1(process, ch1, ch2, ch3)// 杨过学到了来自三位大师的武功(持续学习)for {select {case data := <-result:teacher := "欧阳锋"if string(data[0]) == "a" {teacher = "黄药师"} else if string(data[0]) == "b" {teacher = "洪七公"}fmt.Printf("\t 杨过学到了来自 %s 的武功, 武功是 %s\n", teacher, data)}}
}

杨过具体咋吸收的三位老师的武功?

// chs:多个待合并的channel
func foo1(done chan struct{}, chs ...<-chan string) (_ <-chan string) {result := make(chan string)var wg sync.WaitGroupwriting := func(ch <-chan string) {defer wg.Done()for data := range ch {select {case <-done: // 关闭该ch对应的goroutinereturncase result <- data: // 传递数据到result}}}wg.Add(len(chs))for _, c := range chs {go writing(c) // 每个channel各自操作}go func() {wg.Wait() // 等所有chs被耗尽,本次合并完成,相当于所有数据均已复用到了resultfmt.Println("all ch has used")close(result)}()fmt.Println("returning result...")return result
}

speed running:

returning result...
黄药师 教学:  a1
洪七公 教学:  b1
欧阳锋 教学:  c1杨过学到了来自 黄药师 的武功, 武功是 a1杨过学到了来自 洪七公 的武功, 武功是 b1杨过学到了来自 欧阳锋 的武功, 武功是 c1
黄药师 教学:  a2杨过学到了来自 黄药师 的武功, 武功是 a2
欧阳锋 教学:  c2杨过学到了来自 欧阳锋 的武功, 武功是 c2
洪七公 教学:  b2杨过学到了来自 洪七公 的武功, 武功是 b2
欧阳锋 教学:  c3杨过学到了来自 欧阳锋 的武功, 武功是 c3
黄药师 教学:  a3杨过学到了来自 黄药师 的武功, 武功是 a3
洪七公 教学:  b3杨过学到了来自 洪七公 的武功, 武功是 b3
洪七公 教学:  b4杨过学到了来自 洪七公 的武功, 武功是 b4
欧阳锋 教学:  c4杨过学到了来自 欧阳锋 的武功, 武功是 c4
黄药师 教学:  a4杨过学到了来自 黄药师 的武功, 武功是 a4
洪七公 教学:  b5杨过学到了来自 洪七公 的武功, 武功是 b5
黄药师 教学:  a5杨过学到了来自 黄药师 的武功, 武功是 a5
欧阳锋 教学:  c5杨过学到了来自 欧阳锋 的武功, 武功是 c5
洪七公 教学:  b6杨过学到了来自 洪七公 的武功, 武功是 b6
黄药师 教学:  a6杨过学到了来自 黄药师 的武功, 武功是 a6
欧阳锋 教学:  c6杨过学到了来自 欧阳锋 的武功, 武功是 c6
洪七公 教学:  b7杨过学到了来自 洪七公 的武功, 武功是 b7
黄药师 教学:  a7杨过学到了来自 黄药师 的武功, 武功是 a7
欧阳锋 教学:  c7杨过学到了来自 欧阳锋 的武功, 武功是 c7
洪七公 教学:  b8杨过学到了来自 洪七公 的武功, 武功是 b8
黄药师 教学:  a8杨过学到了来自 黄药师 的武功, 武功是 a8
欧阳锋 教学:  c8杨过学到了来自 欧阳锋 的武功, 武功是 c8

可以看到,三位大师不断教武功给杨过,杨过不断学习武功,教啥学啥,而且是持续学习不想下山赖着不走。

 

变个花样:学成出师

那怎样让杨过学几年后出师呢,学成总要出师嘛,这里模拟三位大师总共教完7套功夫后允许杨过学成出师。一起看看:

func main() {//c1()c2()
}var mu sync.Mutex
var learnedNum int // 已教了的功夫数func isTeachOver() bool {res := falsemu.Lock()if learnedNum >= 7 {res = true}mu.Unlock()return res
}

核心逻辑


// 学成出师, 模拟三位大师总共教完7套功夫后允许杨过学成出师
func c2() {process := make(chan struct{})ch1, ch2, ch3 := make(chan string), make(chan string), make(chan string)once := sync.Once{}teachOverFunc := func(teacher string) bool {res := isTeachOver()if res {if teacher == "黄药师" {fmt.Println("!!!黄药师已停止教学")close(ch1) // 黄药师停止教学} else if teacher == "洪七公" {fmt.Println("!!!洪七公已停止教学")close(ch2)} else {fmt.Println("!!!欧阳锋已停止教学")close(ch3)}once.Do(func() {  // 手段很多,任你随意close(process)fmt.Println("process closed")})}return res}go func() { // 黄药师的指点for i := 1; ; i++ {// 教学之前先看看教完了没有over := teachOverFunc("黄药师")if over {return}select {case <-process:  // 三位老师都会看看是不是有人已经交完了最后一套功夫,教完了就不用再教了fmt.Println("黄药师done")returndefault:// 还没教完,继续教mu.Lock()learnedNum++fmt.Println("x1=", learnedNum)mu.Unlock()data := fmt.Sprintf("a%s", strconv.Itoa(i))fmt.Println("黄药师 教学: ", data)ch1 <- datatime.Sleep(time.Second * 1)}}}()go func() { // 洪七公的教学for i := 1; ; i++ {over := teachOverFunc("洪七公")if over {return}select {case <-process:fmt.Println("洪七公done")returndefault:mu.Lock()learnedNum++fmt.Println("x2=", learnedNum)mu.Unlock()data := fmt.Sprintf("b%s", strconv.Itoa(i))fmt.Println("洪七公 教学: ", data)ch2 <- datatime.Sleep(time.Second * 1)}}}()go func() { // 欧阳锋的教学for i := 1; ; i++ {over := teachOverFunc("欧阳锋")if over {return}select {case <-process:fmt.Println("欧阳锋done")returndefault:mu.Lock()learnedNum++fmt.Println("x3=", learnedNum)mu.Unlock()data := fmt.Sprintf("c%s", strconv.Itoa(i))fmt.Println("欧阳锋 教学: ", data)ch3 <- datatime.Sleep(time.Second * 1)}}}()result := foo2(process, ch1, ch2, ch3)// 杨过学到了来自三位大师的武功(学成出师)yangguoLearned := 0 // 杨过学完可以自己核对一下,自己学到的和老师教的功夫数对不对的上
learning:for {select {case data := <-result:if len(data) > 0 {  teacher := "欧阳锋"if string(data[0]) == "a" {teacher = "黄药师"} else if string(data[0]) == "b" {teacher = "洪七公"}fmt.Printf("\t 杨过学到了来自 %s 的武功, 武功是 %s\n", teacher, data)yangguoLearned++  // 真实学到了功夫才+1,否则直接判断}if yangguoLearned == 7 {if isTeachOver() {fmt.Println("三位老师已教完了所有7套功夫 ", yangguoLearned)}fmt.Println("杨过确认学完了所有7套功夫, 山桃熟了7次,秋叶已红透, 可以下山了")break learning}}}fmt.Println("教学与学习流程结束.")time.Sleep(time.Second * 20) // 杨过干点其他啥(仅为了便于观察程序运行控制而模拟继续运行,真正程序业务运行中不会直接停止主线程)}

杨过具体咋吸收的三位老师的武功?

func foo2(done chan struct{}, chs ...<-chan string) (_ <-chan string) {result := make(chan string)var wg sync.WaitGroupwriting := func(ch <-chan string) {defer wg.Done()for data := range ch {select {case <-done:returncase result <- data:}}}wg.Add(len(chs))for _, c := range chs {go writing(c)}go func() {wg.Wait() // 等待秋叶红透,本次教学完成fmt.Println("all ch has used")close(result)}()fmt.Println("returning result...")return result
}

来看看教学与学习结果,是否能成功出师:

returning result...
x3= 1
欧阳锋 教学:  c1杨过学到了来自 欧阳锋 的武功, 武功是 c1
x1= 2
黄药师 教学:  a1杨过学到了来自 黄药师 的武功, 武功是 a1
x2= 3
洪七公 教学:  b1杨过学到了来自 洪七公 的武功, 武功是 b1
x3= 4
欧阳锋 教学:  c2杨过学到了来自 欧阳锋 的武功, 武功是 c2
x1= 5
黄药师 教学:  a2
x2= 6
洪七公 教学:  b2杨过学到了来自 黄药师 的武功, 武功是 a2杨过学到了来自 洪七公 的武功, 武功是 b2
x3= 7
欧阳锋 教学:  c3杨过学到了来自 欧阳锋 的武功, 武功是 c3
三位老师已教完了所有7套功夫  7
杨过确认学完了所有7套功夫, 山桃熟了7次,秋叶已红透, 可以下山了
教学与学习流程结束.
!!!洪七公已停止教学
process closed
!!!黄药师已停止教学
!!!欧阳锋已停止教学
all ch has used

是不是很有意思呢?

 

哈哈,这次先到这。再会!


 

 

 

相关文章:

go并发之美·多个channel合并/多个数据流合并

多个数据流&#xff08;来自于不同channel&#xff09;合并为一个流。 一般用于多个相同性质来源的数据进行合并为一处进行统一处理。 目录 背景 实现赖着不走 变个花样&#xff1a;学成出师 背景 最近在重温武侠剧&#xff0c;无意间想到了一些情形然后手痒&#xff0c;想…...

数据库多租户实现三种方式

1960年&#xff0c;许多公司需要使用更多的运算资源&#xff0c;向持有Mainframe的供应商租用运算资源。与此同时&#xff0c;Mainframe的供应商会根据用户登录系统时输入的数据匹配ID&#xff0c;利用ID来计算运算的资源使用量&#xff0c;包含CPU&#xff0c;存储器&#xff…...

单协议 2.4GHz CC2651R31T0RGZR/CC2651R31T0RKPR无线MCU 802.15.4,蓝牙5.2

CC2651R31T0RGZR描述&#xff1a;具有 352KB 闪存的 SimpleLink 32 位 Arm Cortex-M4 单协议 2.4GHz 无线 MCU 48-VQFN -40C ~ 105C48QFN&#xff08;明佳达电子&#xff09;【介绍】CC2651R3器件是一款单协议 2.4 GHz 无线微控制器 (MCU)&#xff0c;支持以下协议&#xff1a;…...

【项目精选】基于struts+hibernate的采购管理系统

点击下载 javaEE采购管理系统 本系统是一个独立的系统&#xff0c;用来解决企业采购信息的管理问题。采用JSP技术构建了一个有效而且实用的企业采购信息管理平台&#xff0c;目的是为高效地完成对企业采购信息的管理。经过 对课题的深入分析&#xff0c;采购系统需实现以下功能…...

在找docker命令和部署?看这一篇文章就够了。

一、docker 常用命令 docker ps -a #查看所有容器 docker images #查看所有images docker search rabbitmq #搜索rabbitmq docker pull rabbitmq #拉去rabbitmq docker run -id --namemy_rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq # 创建一个容器并启动 docker exec -it…...

NTLM协议原理分析

LM Hash 和 NTLM Hashwindows用户的密码以哈希的形式保存在SAM文件中“%SystemRoot%\system32\config\SAM”。域用户的密码以哈希的形式保存在域控的 NTDS.dit 文件中。 密码的哈希值格式如下用域名:uid:LM哈希:NTLM哈希:::由于LM Hash 有安全缺陷&#xff0c;所以Windows Vist…...

SOC计算方法:电流积分+开路电压

最近小猿在学习soc的计算方法&#xff0c;soc的估算方法大致有五种&#xff1a;电流积分法、开路电压法、阻抗法、智能估算法、状态观测器。今天先给大家介绍前两种方法。 什么是SOC 电池的状态&#xff08;State of Charge&#xff0c;SOC&#xff09;是电池能够提供的电荷总…...

linux mysql启动报错处理方案

启动命令&#xff1a; systemctl start mysqld 一、关闭selinux setenforce 0 二、...

Qt配置VS的编译环境(以MSVC2015 64bit为例)

目录 一、原因 二、VS2015安装 三、配置套件&#xff08;Kits&#xff09; 一、原因 很多时候&#xff0c;由于VS版本切换&#xff0c;需要从高版本切换到低版本&#xff0c;或者从低版本升级到高版本&#xff0c;例如VS2019到VS2015&#xff0c;或者VS2010到VS2015。 以VS2…...

iOS 9.3.5越狱环境安装配置

前言 家里有几个iOS设备&#xff0c;iTouch&#xff0c;iPad&#xff0c;都老旧了&#xff0c;正好弄来搭建开发环境。 目标&#xff1a;在iOS越狱环境上搭建基本的软件&#xff0c;将它变成小型Unix服务器和一个能开发iOS应用的环境。 什么是iOS越狱&#xff08;iOS Jailbre…...

mac电脑解决Error: command failed: npm install --loglevel error --legacy-peer-deps

使用vue create xxx创建vue3项目的时候报错。 解决步骤&#xff1a; 1.sudo npm cache clean --force 2.再次创建就可以成功 补充&#xff1a;网上搜到很多方法&#xff0c;都尝试失败&#xff0c;因为遇到需要打开.vuerc,.npmrc的情况&#xff0c;记录一下怎样找到文件 1. 尝…...

Java中对象的finalization机制

本篇文章我们详细介绍Java中对象的finalization机制&#xff0c;以及怎么使用finalize()方法&#xff0c;将即将被回收的对象&#xff0c;拉回来。1、finalization机制Java语言提供了对象终止&#xff08;finalization&#xff09;机制来允许开发人员提供对象被销毁之前的自定义…...

proteus光敏电阻电路的arduino仿真

虽然Fritzing0.9.10有了仿真的功能&#xff0c;但都是测试板&#xff0c;能够仿真的很有限&#xff0c;所以还是要借助proteus来仿真。这里&#xff0c;我们来实先一个简单的光明电阻的仿真电路。本篇博文&#xff0c;重点演示proteus仿真arduino光敏电阻&#xff0c;arduino采…...

MySql面试精选—慢查询如何优化

目录 1、如何界定是慢查询SQL 2、如何快速定位低效率SQL 1)查看慢SQL语句...

一款OutLook信息收集工具

OutLook 这是一款burp插件&#xff0c;用于Outlook用户信息收集&#xff0c;在已登录Outlook账号后&#xff0c;可以使用该 插件自动爬取所有联系人的信息 安装 在burp扩展面板加载jar即可 功能介绍 All Users 加载插件后&#xff0c;进入Outlook联系人面板&#xff0c;…...

java多线程(二一)并发协作生产者消费者设计模式

1.两个线程一个生产者一个消费者 需求情景 两个线程&#xff0c;一个负责生产&#xff0c;一个负责消费&#xff0c;生产者生产一个&#xff0c;消费者消费一个。 涉及问题 同步问题&#xff1a;如何保证同一资源被多个线程并发访问时的完整性。常用的同步方法是采用标记或加…...

Win YAPI + Jenkins 实现接口自动化测试

自动化测试 传统的接口自动化测试成本高&#xff0c;大量的项目没有使用自动化测试保证接口的质量&#xff0c;仅仅依靠手动测试&#xff0c;是非常不可靠和容易出错的。 为了解决这个问题&#xff0c;使用YAPI接口自动化测试功能&#xff0c;只需要配置每个接口的入参和对 RE…...

【计算机视觉 自然语言处理】什么是多模态?

文章目录一、多模态的定义二、多模态的任务2.1 VQA&#xff08;Visual Question Answering&#xff09;视觉问答2.2 Image Caption 图像字幕2.3 Referring Expression Comprehension 指代表达2.4 Visual Dialogue 视觉对话2.5 VCR (Visual Commonsense Reasoning) 视觉常识推理…...

2023百度面试真题

【百度】面试真题&#xff1a; 1、SpingBoot 也有定时任务&#xff1f;是什么注解&#xff1f; 在 SpringBoot 中使用定时任务主要有两种不同的方式&#xff0c;一个就是使用 Spring 中的Scheduled 注解&#xff0c;另一个则是使用第三方框架 Quartz。 使用 Spring 中的 Sch…...

MAC(m1)-VMWare Fushion安装Windows11

镜像下载地址:登录 账号:11360XXXXX@qq.com 密码:ZXXXSXX19XX 参考:VMware fusion虚拟机安装Win10系统的详细教程_IT大力水手的博客-CSDN博客_vmware fusion安装 uefi和bios有什么区别?uefi和bios的区别详细分析 _ 电脑系统城 设置密码...

渲染学进阶内容——模型

最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...

华为OD机试-食堂供餐-二分法

import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...

Python爬虫(一):爬虫伪装

一、网站防爬机制概述 在当今互联网环境中&#xff0c;具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类&#xff1a; 身份验证机制&#xff1a;直接将未经授权的爬虫阻挡在外反爬技术体系&#xff1a;通过各种技术手段增加爬虫获取数据的难度…...

中医有效性探讨

文章目录 西医是如何发展到以生物化学为药理基础的现代医学&#xff1f;传统医学奠基期&#xff08;远古 - 17 世纪&#xff09;近代医学转型期&#xff08;17 世纪 - 19 世纪末&#xff09;​现代医学成熟期&#xff08;20世纪至今&#xff09; 中医的源远流长和一脉相承远古至…...

面向无人机海岸带生态系统监测的语义分割基准数据集

描述&#xff1a;海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而&#xff0c;目前该领域仍面临一个挑战&#xff0c;即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...

宇树科技,改名了!

提到国内具身智能和机器人领域的代表企业&#xff0c;那宇树科技&#xff08;Unitree&#xff09;必须名列其榜。 最近&#xff0c;宇树科技的一项新变动消息在业界引发了不少关注和讨论&#xff0c;即&#xff1a; 宇树向其合作伙伴发布了一封公司名称变更函称&#xff0c;因…...

基于PHP的连锁酒店管理系统

有需要请加文章底部Q哦 可远程调试 基于PHP的连锁酒店管理系统 一 介绍 连锁酒店管理系统基于原生PHP开发&#xff0c;数据库mysql&#xff0c;前端bootstrap。系统角色分为用户和管理员。 技术栈 phpmysqlbootstrapphpstudyvscode 二 功能 用户 1 注册/登录/注销 2 个人中…...

第一篇:Liunx环境下搭建PaddlePaddle 3.0基础环境(Liunx Centos8.5安装Python3.10+pip3.10)

第一篇&#xff1a;Liunx环境下搭建PaddlePaddle 3.0基础环境&#xff08;Liunx Centos8.5安装Python3.10pip3.10&#xff09; 一&#xff1a;前言二&#xff1a;安装编译依赖二&#xff1a;安装Python3.10三&#xff1a;安装PIP3.10四&#xff1a;安装Paddlepaddle基础框架4.1…...

怎么开发一个网络协议模块(C语言框架)之(六) ——通用对象池总结(核心)

+---------------------------+ | operEntryTbl[] | ← 操作对象池 (对象数组) +---------------------------+ | 0 | 1 | 2 | ... | N-1 | +---------------------------+↓ 初始化时全部加入 +------------------------+ +-------------------------+ | …...

Linux-进程间的通信

1、IPC&#xff1a; Inter Process Communication&#xff08;进程间通信&#xff09;&#xff1a; 由于每个进程在操作系统中有独立的地址空间&#xff0c;它们不能像线程那样直接访问彼此的内存&#xff0c;所以必须通过某种方式进行通信。 常见的 IPC 方式包括&#…...