等待所有任务完成后再关闭通道的方法
来源:stackoverflow
时间:2024-02-29 22:33:24 178浏览 收藏
编程并不是一个机械性的工作,而是需要有思考,有创新的工作,语法是固定的,但解决问题的思路则是依靠人的思维,这就需要我们坚持学习和更新自己的知识。今天golang学习网就整理分享《等待所有任务完成后再关闭通道的方法》,文章讲解的知识点主要包括,如果你对Golang方面的知识点感兴趣,就不要错过golang学习网,在这可以对大家的知识积累有所帮助,助力开发能力的提升。
我正在用 go 创建一个电子邮件发送应用程序。
我遇到了无法成功发送电子邮件的问题。在这些情况下,我想在 go 频道中返回电子邮件,但该频道已关闭。
如何防止通道在所有任务完成之前关闭?
这是存在此问题的函数:
func worker(toSend chan combo, tried chan combo, s smptInfo, wg *sync.WaitGroup) { for try := range toSend { startSend := time.Now() delegate := make(chan bool, 1) go sendEmailSSH(try, delegate, s, try.line) select { case res := <-delegate: if res == true { try.success = res tried <- try } else { toSend <- try // if send not successfull return back to channel } case <-time.After(smtpTimeout): toSend <- try // if send timeout return back to channel } Pauza(startSend) // if send more fast like limit send, wait. } wg.Done() }
完整的应用程序可以在 github 上找到。
解决方案
这是我建议的方法。为了简单起见,我删除了一些 smtp 特定部分。您可以再次插入它们。
1。示例代码:
package main import ( "fmt" "log" "math/rand" "sync" "time" ) type combo struct { success bool toEmail string } func sendEmailSSH(try combo, delegate chan bool) { defer close(delegate) r := rand.Intn(7) time.Sleep(time.Duration(r) * time.Second) if r%2 == 0 { fmt.Printf("sendEmailSSH: failing randomly after %d seconds: %+v\n", r, try) delegate <- false } else { fmt.Printf("sendEmailSSH: successful after %d seconds: %+v\n", r, try) delegate <- true } } func sendEmailSSHWrapper(try combo, toRetry chan combo) { // do something with try delegate := make(chan bool, 1) smtpTimeout := 3 // if no success within 3 seconds, mark it as failed go sendEmailSSH(try, delegate) go func() { select { case res := <-delegate: if res == true { try.success = res } else { toRetry <- try // if send not successfull, add to retry channel } case <-time.After(time.Duration(smtpTimeout) * time.Second): fmt.Printf("sendEmailSSHWrapper: failing due to timeout: %+v\n", try) toRetry <- try // if send timeout, add to retry channel } }() } // where we actually do the work func worker(toSend chan combo, wg *sync.WaitGroup, id int) { toRetry := make(chan combo, 5) retryDone := false for try := range toSend { go sendEmailSSHWrapper(try, toRetry) } for !retryDone { select { case try, ok := <-toRetry: if !ok { log.Println("toRetry is already closed") retryDone = true continue } fmt.Printf("worker %d: picking up %+v for retry\n", id, try) go sendEmailSSHWrapper(try, toRetry) case <-time.After(time.Duration(15) * time.Second): fmt.Printf("worker %d: no data in toRetry(%d) channel for 15 seconds. closing toRetry now\n", id, id) close(toRetry) toRetry = nil retryDone = true } } wg.Done() } func main() { var wg sync.WaitGroup // keep track of the workers toSend := make(chan combo) // to the workers // emailList := []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"} emailList := []string{"a", "b", "c", "d", "e", "f"} // initialize n workers for i := 0; i < 5; i++ { wg.Add(1) go worker(toSend, &wg, i) } var count int = 0 for i := 0; i < len(emailList); i++ { count++ toSend <- combo{toEmail: emailList[i], success: false} } close(toSend) wg.Wait() fmt.Println("Send emails Done!") }
在这里尝试一下: https://play.golang.org/p/W7ur2wr3HeD
2。我所做的:
- 每当 main 完成添加数据时,它应该关闭
tosend
通道。不会再使用它。 - 创建 sendemailssh 函数的包装器。您当前调用
go sendemailssh(try, delegate, s, try.line)
的实现不是很优化,因为在启动 go 例程后,它无论如何都会被阻塞在一元大小的通道上,这与串行调用一样好。 - 或者,我调用了
go sendemailsshwrapper(try, toretry)
,它在内部调用实际的sendemailsshwrapper
函数并监视委托通道。 sendemailssh
随机失败少数请求,用于演示目的。sendemailsshwrapper
监视delegate
它自己的通道是否出现故障,并检查超时(我将其保留为 3 秒以进行演示)- 工作线程首先完全耗尽
tosend
通道。然后移动到toretry
频道。它可以正常工作,除非它在该通道上 15 秒内没有看到任何数据(再次保持高电平以进行演示)
3。您需要添加的内容:
您确实应该实施某种机制,以便对任何给定的电子邮件地址仅重试给定的次数。否则,您可能会永远陷入 for 循环,对于任何错误的电子邮件地址或任何意外情况(由于延迟而导致多次连续超时),可能会使用映射来跟踪重试条目的计数。或者更简单,将
retrycount int
添加到combo
结构中,并在每次重试时递增它,并在每次重试之前进行检查。请注意,在您和我的方法中,我们都会在超时时重试。您确定如果发生超时,电子邮件将永远不会送达吗?如果没有,您确实应该使用带有取消 的
context,或者在重试之前,如果需要更多时间,则以某种方式终止实际的 go 例程。否则,在某个糟糕的日子,如果延迟较长,您可能最终会超时很多,并继续重试。而实际上,您会向用户发送大量垃圾邮件。
好了,本文到此结束,带大家了解了《等待所有任务完成后再关闭通道的方法》,希望本文对你有所帮助!关注golang学习网公众号,给大家分享更多Golang知识!
-
502 收藏
-
502 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
139 收藏
-
204 收藏
-
325 收藏
-
477 收藏
-
486 收藏
-
439 收藏
-
357 收藏
-
352 收藏
-
101 收藏
-
440 收藏
-
212 收藏
-
143 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 508次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习