分享
  1. 首页
  2. 文章

channel和select控制goroutines

kevin · · 2053 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

最近有一个需求是在一个常驻内存的程序中结束相关任务。在Go中,创建一个goroutine非常简单,只需要go一下就可以了,但是如果我创建了很多goroutine,想要结束怎么办?

比如说我有一个死循环的例子

packagemainimport("fmt""sync""time")funcmain(){queue:=make(chanint,20)varwgsync.WaitGroupfor{// 就是一个死循环queue<-1<-queuefori:=0;i<10;i++{wg.Add(1)gofunc(iint){time.Sleep(5*time.Second)fmt.Println("Sleep")wg.Done()}(i)}wg.Wait()}}

如何在一个goroutine里面控制所有的goroutine,让所有的goroutine结束呢?这就需要select出场了。有人告诉我,这样子实现会更好一些:

packagemainimport("fmt""sync""time")funcmain(){// 定义一个用于指定退出的channelEXIT:=make(chanint,1)queue:=make(chanint,20)varwgsync.WaitGroup// 启动新的goroutine gofunc(){time.Sleep(10*time.Second)// 休息了之后,该结束了EXIT<-1}()for{// 进入死循环queue<-1select{case<-EXIT:// 收到了退出消息fmt.Println("KILLED")returncase<-queue:fori:=0;i<10;i++{wg.Add(1)gofunc(iint){time.Sleep(5*time.Second)fmt.Println("Sleep")wg.Done()}(i)}wg.Wait()}}}

但是输出却是比较让人困惑:

F:\>gorundada.goSleepSleepSleepSleepSleepSleepSleepSleepSleepSleepSleepSleepSleepSleepSleepSleepSleepSleepSleepSleepSleepSleepSleepSleepSleepSleepSleepSleepSleepSleepKILLED

看起来停止的时间有15s,比预想的10s时间要长一些,这是为什么呢?

这里select的作用是,在遇到channel case时,尝试所有的channel是否为ready;若有一个为ready,则执行该case,多个case时会随机执行其中一个case;如果有default,则会在所有都not ready时执行;没有default的话就wait等待ready。

关于select的随机性,用一个例子来说明更方便一些:

packagemainimport("time""fmt")funcmain(){// 定义一个用于指定退出的channelc1:=make(chanint,1)c2:=make(chanint,1)for{c1<-1c2<-1select{case<-c1:fmt.Println("c1")// 防止出现panic<-c2time.Sleep(1*time.Second)case<-c2:fmt.Println("c2")<-c1time.Sleep(1*time.Second)}}}

输出结果如下:

gorundada.goc1c1c2c1c1c2c1....

那这样上面还是会出现那种有可能没退出的情况,这样怎么做呢?下面我个人感觉会是一种更好的做法:

packagemainimport("fmt""sync""time")funcmain(){// 定义一个用于指定退出的channelEXIT:=make(chanint,1)varwgsync.WaitGroup// 启动新的goroutine gofunc(){time.Sleep(10*time.Second)// 休息了之后,该结束了EXIT<-1}()for{select{case<-EXIT:// 收到了退出消息fmt.Println("KILLED")returndefault:fori:=0;i<10;i++{wg.Add(1)gofunc(iint){time.Sleep(5*time.Second)fmt.Println("Sleep")wg.Done()}(i)}wg.Wait()}}}

还有一个值得注意的事情就是,对select来说,整体的运行相当于一个循环分支处理的过程。对case来说,过程是一个block的过程,比如说在执行default过程中,即使收到了来自EXIT的信息,也不会中断执行default去跳转执行EXIT,而是在default完成之后,进入条件分支选择使优先进入channel已经ready的case。


有疑问加站长微信联系(非本文作者)

本文来自:ipfans's Blog

感谢作者:kevin

查看原文:channel和select控制goroutines

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
2053 次点击
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏