首页 > 其他分享 >在 Go 中收听多个频道

在 Go 中收听多个频道

时间:2022-09-06 09:15:01浏览次数:85  
标签:语句 频道 chan make goroutine 收听 Go c1 我们

在 Go 中收听多个频道

欢迎回到系列!今天,我们将研究同时收听多个频道的方法。之前的指南帮助您开始使用 Go 中的并发性。尽管简单的方法通常是最好的方法,但您可能一直在尝试实现更复杂的行为。阅读本指南后,您将能够使您的并发代码更加灵活。

选择关键字

我们可以使用 选择 关键字一次收听多个 goroutine。

 包主 进口 (  
 “fmt”  
 “时间”  
 ) 功能主要(){  
 c1 := make(chan 字符串)  
 c2 := make(chan 字符串) 去函数(){  
 time.Sleep(1 * time.Second)  
 c1 <- time.Now().String()  
 }() 去函数(){  
 time.Sleep(2 * time.Second)  
 c2 <- time.Now().String()  
 }() 对于我:= 0;我 < 2;我++ {  
 选择 {  
 案例 res1 := <-c1:  
 fmt.Println("来自 c1:", res1)  
 案例 res2 := <-c2:  
 fmt.Println("来自 c2:", res2)  
 }  
 }  
 } 来自 c1:2022-09-04 14:30:39.4469184 -0400 EDT m=+1.000172801  
 来自 c2:2022-09-04 14:30:40.4472699 -0400 EDT m=+2.000524401

上面的代码显示了如何 选择 关键字有效。

  • 我们首先创建两个通道 c1 c2 听。
  • 然后我们生成两个 goroutine,每个都将当前时间发送到 c1 c2 .
  • 在 for 循环中,我们创建一个 选择 声明并定义两种情况:第一种情况是我们何时可以收到来自 c1 第二个是我们什么时候可以收到 c2 .

你可以看到 选择 声明在设计上与 转变 陈述。两者都定义了不同的情况,并在满足特定情况时运行各自的代码。此外,我们可以看到 选择 语句被阻塞。也就是说,它将等到满足其中一种情况。

我们为循环迭代了两次,因为只有两个 goroutine 需要监听。更准确地说,每个 goroutine 都是一个即发即弃的 goroutine,这意味着它们在返回之前只向一个通道发送一次。因此,这段代码中始终最多有两条消息,我们只需要选择两次。

如果我们不知道工作何时结束怎么办?

有时我们不知道有多少工作。在这种情况下,将 选择 while 循环中的语句。

 包主 进口 (  
 “fmt”  
 “数学/兰特”  
 “时间”  
 ) 功能主要(){  
 c1 := make(chan 字符串)  
 rand.Seed(time.Now().UnixNano()) 对于我:= 0;我 < rand.Intn(10);我++ {  
 去函数(){  
 time.Sleep(1 * time.Second)  
 c1 <- time.Now().String()  
 }()  
 } 为了 {  
 选择 {  
 案例 res1 := <-c1:  
 fmt.Println("来自 c1:", res1)  
 }  
 }  
 }

因为我们让随机数量的 goroutine 运行,所以我们不知道有多少作业。值得庆幸的是,底部包含 select 语句的 for 循环将捕获每个输出。让我们看看如果我们运行这段代码会发生什么。

 从 c1: 2022-09-04 14:48:47.5145341 -0400 EDT m=+1.000257801  
 从 c1: 2022-09-04 14:48:47.5146126 -0400 EDT m=+1.000336201  
 从 c1: 2022-09-04 14:48:47.5146364 -0400 EDT m=+1.000359901  
 致命错误:所有 goroutine 都处于休眠状态 - 死锁! goroutine 1 [chan 接收]:  
 main.main()  
 /home/jacob/blog/testing/listening-to-multiple-channels-in-go/main.go:22 +0x128  
 退出状态 2

嗯,select语句按预期接收了3次,但是程序因为死锁而报错了。为什么会这样?

请记住,在没有任何条件的情况下,Go 中的 for 循环将永远运行。这意味着 select 语句将尝试永远接收。但是,要运行的作业数量是有限的。即使没有更多作业,select 语句仍会尝试接收。

还记得在本系列的第一篇文章中我说过,如果您在发送方未准备好时尝试从无缓冲通道接收,您的程序将陷入死锁吗?这正是我们示例中的情况。

那么我们如何解决这个问题呢?我们可以结合使用之前文章中介绍的概念:退出通道和 WaitGroups。

 包主 进口 (  
 “fmt”  
 “数学/兰特”  
 “同步”  
 “时间”  
 ) 功能主要(){  
 c1 := make(chan 字符串)  
 退出 := make(chan struct{})  
 rand.Seed(time.Now().UnixNano())  
 var wg sync.WaitGroup 去函数(){  
 numJob := rand.Intn(10)  
 fmt.Println("作业数:", numJob)  
 对于我:= 0;我<numJob;我++ {  
 wg.Add(1)  
 去函数(){  
 推迟 wg.Done()  
 time.Sleep(1 * time.Second)  
 c1 <- time.Now().String()  
 }()  
 }  
 wg.Wait()  
 关闭(退出)  
 }() 为了 {  
 选择 {  
 案例 res1 := <-c1:  
 fmt.Println("来自 c1:", res1)  
 案例<-退出:  
 返回  
 }  
 }  
 } 3  
 从 c1: 2022-09-04 15:09:08.6936976 -0400 EDT m=+1.000287801  
 从 c1: 2022-09-04 15:09:08.6937788 -0400 EDT m=+1.000369101  
 从 c1: 2022-09-04 15:09:08.6937949 -0400 EDT m=+1.000385101
  • 我们创建了一个退出通道和一个 WaitGroup。
  • 每次运行的作业数量是随机的。为了 numJobs 很多次,我们会触发 goroutines。为了等待作业完成,我们将它们添加到 工作组 .当一项工作完成后,我们从 工作组 .
  • 完成所有作业后,我们关闭退出通道。
  • 我们将上面的部分包装在一个 goroutine 中,因为我们希望所有这些都是非阻塞的。如果我们不将它包装在 goroutine 中, wg.Wait() 将等到工作完成。这将阻止代码,并且不会让底部的 for-select 语句运行。
  • 此外,由于 c1 是一个无缓冲的通道,等待所有的 goroutine 发送消息到 c1 将导致许多消息被发送到 c1 没有 for-select 语句来接收它们。这会导致死锁,因为当发送者准备好时,接收者还没有准备好。

如何使选择非阻塞

选择 语句默认是阻塞的。我们如何使这个非阻塞?这很简单——我们只是添加一个默认情况。

 包主 进口 (  
 “fmt”  
 “数学/兰特”  
 “同步”  
 “时间”  
 ) 功能主要(){  
 ashleyMsg := make(chan 字符串)  
 brianMsg := make(chan 字符串)  
 退出 := make(chan struct{})  
 rand.Seed(time.Now().UnixNano())  
 var wg sync.WaitGroup 去函数(){  
 numJob := rand.Intn(10)  
 fmt.Println("作业数:", numJob)  
 对于我:= 0;我<numJob;我++ {  
 wg.Add(2)  
 去函数(){  
 推迟 wg.Done()  
 time.Sleep(time.Duration(rand.Intn(10)) * time.Millisecond)  
 ashleyMsg <-“嗨”  
 }()  
 去函数(){  
 推迟 wg.Done()  
 time.Sleep(time.Duration(rand.Intn(10)) * time.Millisecond)  
 brianMsg <-“怎么了”  
 }()  
 }  
 wg.Wait()  
 关闭(退出)  
 }() 为了 {  
 选择 {  
 案例 res1 := <-ashleyMsg:  
 fmt.Println("阿什利:", res1)  
 案例 res2 := <-brianMsg:  
 fmt.Println("布莱恩:", res2)  
 案例<-退出:  
 fmt.Println("聊天结束")  
 返回  
 默认:  
 fmt.Println("...")  
 时间.睡眠(时间.毫秒)  
 }  
 }  
 } ...  
 工作数量:4  
 布莱恩:怎么了  
 ...  
 阿什利:嗨  
 ...  
 ...  
 布莱恩:怎么了  
 阿什利:嗨  
 阿什利:嗨  
 布莱恩:怎么了  
 ...  
 ...  
 阿什利:嗨  
 ...  
 布莱恩:怎么了  
 ...  
 聊天结束

除了蹩脚的对话之外,我们还可以看到默认案例是如何工作的。我们可以在没有可接收的渠道时做一些事情,而不是等待聊天到达。在这个例子中,我们只是打印出椭圆,但你可以做任何你想做的事情。

结论

这就是这篇文章!现在您可以同时收听多个频道,这在您开发个人项目时可能是一笔巨大的财富。感谢阅读,我们下期再见。

你也可以阅读这篇文章 开发者 我的个人网站 .

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明

本文链接:https://www.qanswer.top/18514/55550608

标签:语句,频道,chan,make,goroutine,收听,Go,c1,我们
From: https://www.cnblogs.com/amboke/p/16660444.html

相关文章

  • 创建一个 Golang 库
    创建一个Golang库使用go模块在本文中,您将学习如何使用gomods创建自己的库并将其导入所需的项目。首先让我向您介绍一下gomodules是什么。“模块是存储在文件......
  • [Go] Method
    //UsertypeUserstruct{ IDint FirstNamestring}//Methodfunc(u*User)describe()string{ desc:=fmt.Sprintf("Name%s",u.FirstName) retur......
  • [Google] LeetCode 2172 Maximum AND Sum of Array 状态压缩DP
    YouaregivenanintegerarraynumsoflengthnandanintegernumSlotssuchthat2*numSlots>=n.TherearenumSlotsslotsnumberedfrom1tonumSlots.You......
  • [Go] Error
    packagemainimport( "errors" "fmt" "strings")funcshouldGreaterThanTen(numint)error{ ifnum<10{ returnerrors.New("Numberislessthan10") ......
  • [Go] Defer, panic, recover
    packagemainimport( "fmt")funcrecoverFromPanic(){ fmt.Println("defer")}funcdoThings(){ deferrecoverFromPanic() fori:=0;i<5;i++{ fmt......
  • [GO] Pass by reference
    funcchangeName(name*string){ *name=strings.ToUpper(*name)}//CoordinatestypeCoordinatesstruct{ X,Yfloat64}funcmain(){ name:="Elvis" ch......
  • [Go] Pointer
    varnamestring varnamePointer*string//Pointer name="Beyonce" namePointer=&name//AssignaPointer fmt.Println("Name:",name) fmt.Println("Nam......
  • django ORM常见查询关键字与外键字段数据操作
    今日内容表查询数据准备及测试环境搭建1.django自带一个sqllite3小型数据库 该数据库功能非常有限并且针对日期类型的数据兼容性很差2.django切换MySQL数据 针对dja......
  • django框架之字段类型及表查询
    ORM操作数据准备及测试环境搭建1.数据库切换:配置文件settings中配置mysql数据库DATABASES={'default':{'ENGINE':'django.db.backends.mysql',......
  • Django ORM 常用字段和表查询
    表查询数据准备及测试环境搭建sqllite3django自带sqllite3小型数据库该数据库功能非常有限,并且针对日期类型的数据兼容性很差django切换MySQL数据django1.x版本的......