GoLang channel使用介绍

 更新时间:2022年10月11日 10:29:36   作者:~庞贝  
Channel 和 goroutine 的结合是 Go 并发编程的大杀器。而 Channel 的实际应用也经常让人眼前一亮,通过与 select,cancel,timer 等结合,它能实现各种各样的功能。接下来,我们就要梳理一下 channel 的应用

停止信号

channel 用于停止信号的场景还是挺多的,经常是关闭某个 channel 或者向 channel 发送一个元素,使得接收 channel 的那一方获知道此信息,进而做一些其他的操作。

任务定时

与 timer 结合,一般有两种玩法:实现超时控制,实现定期执行某个任务。

有时候,需要执行某项操作,但又不想它耗费太长时间,上一个定时器就可以搞定:

select {
	case <-time.After(100 * time.Millisecond):
	case <-s.stopc:
		return false
}

等待 100 ms 后,如果 s.stopc 还没有读出数据或者被关闭,就直接结束。这是来自 etcd 源码里的一个例子,这样的写法随处可见。

定时执行某个任务,也比较简单:

func worker() {
	ticker := time.Tick(1 * time.Second)
	for {
		select {
		case <- ticker:
			// 执行定时任务
			fmt.Println("执行 1s 定时任务")
		}
	}
}

每隔 1 秒种,执行一次定时任务。

解耦生产方和消费方

服务启动时,启动 n 个 worker,作为工作协程池,这些协程工作在一个 for {} 无限循环里,从某个 channel 消费工作任务并执行:

func main() {
	taskCh := make(chan int, 100)
	go worker(taskCh)
    // 塞任务
	for i := 0; i < 10; i++ {
		taskCh <- i
	}
    // 等待 1 小时 
	select {
	case <-time.After(time.Hour):
	}
}
func worker(taskCh <-chan int) {
	const N = 5
	// 启动 5 个工作协程
	for i := 0; i < N; i++ {
		go func(id int) {
			for {
				task := <- taskCh
				fmt.Printf("finish task: %d by worker %d\n", task, id)
				time.Sleep(time.Second)
			}
		}(i)
	}
}

5 个工作协程在不断地从工作队列里取任务,生产方只管往 channel 发送任务即可,解耦生产方和消费方。

finish task: 1 by worker 4
finish task: 2 by worker 2
finish task: 4 by worker 3
finish task: 3 by worker 1
finish task: 0 by worker 0
finish task: 6 by worker 0
finish task: 8 by worker 3
finish task: 9 by worker 1
finish task: 7 by worker 4
finish task: 5 by worker 2

控制并发数

有时需要定时执行几百个任务,例如每天定时按城市来执行一些离线计算的任务。但是并发数又不能太高,因为任务执行过程依赖第三方的一些资源,对请求的速率有限制。这时就可以通过 channel 来控制并发数。

下面的例子来自《Go 语言高级编程》:

var limit = make(chan int, 3)
func main() {
    // …………
    for _, w := range work {
        go func() {
            limit <- 1
            w()
            <-limit
        }()
    }
    // …………
}

构建一个缓冲型的 channel,容量为 3。接着遍历任务列表,每个任务启动一个 goroutine 去完成。真正执行任务,访问第三方的动作在 w() 中完成,在执行 w() 之前,先要从 limit 中拿“许可证”,拿到许可证之后,才能执行 w(),并且在执行完任务,要将“许可证”归还。这样就可以控制同时运行的 goroutine 数。

这里,limit <- 1 放在 func 内部而不是外部,原因是:

如果在外层,就是控制系统 goroutine 的数量,可能会阻塞 for 循环,影响业务逻辑。

limit 其实和逻辑无关,只是性能调优,放在内层和外层的语义不太一样。

还有一点要注意的是,如果 w() 发生 panic,那“许可证”可能就还不回去了,因此需要使用 defer 来保证。

到此这篇关于GoLang channel使用介绍的文章就介绍到这了,更多相关Go channel内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Go语言中interface语法与使用详解

    Go语言中interface语法与使用详解

    Go语言里面设计最精妙的应该算interface,它让面向对象,内容组织实现非常的方便,下面这篇文章主要给大家介绍了关于Go语言中interface语法与使用的相关资料,需要的朋友可以参考下
    2022-07-07
  • go使用net/url包来解析URL提取主机部分

    go使用net/url包来解析URL提取主机部分

    这篇文章主要为大家介绍了go使用net/url包来解析URL提取主机部分实例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2024-01-01
  • Go语言操作etcd的示例详解

    Go语言操作etcd的示例详解

    etcd是使用Go语言开发的一个开源的、高可用的分布式key—value存储系统,可以用于配置共享和服务的注册和发现,下面我们就来看看Go语言是如何操作etcd的吧
    2024-03-03
  • Golang中匿名组合实现伪继承的方法

    Golang中匿名组合实现伪继承的方法

    这篇文章主要介绍了Golang中匿名组合实现伪继承的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-08-08
  • 总结Golang四种不同的参数配置方式

    总结Golang四种不同的参数配置方式

    这篇文章主要介绍了总结Golang四种不同的参数配置方式,文章围绕主题展开详细的内容戒杀,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-09-09
  • 解决golang编译提示dial tcp 172.217.160.113:443: connectex: A connection attempt failed(推荐)

    解决golang编译提示dial tcp 172.217.160.113:443: con

    这篇文章主要介绍了解决golang编译提示dial tcp 172.217.160.113:443: connectex: A connection attempt failed,此问题完美解决,需要的朋友可以参考下
    2023-02-02
  • golang设置http response响应头与填坑记录

    golang设置http response响应头与填坑记录

    这篇文章主要给大家介绍了关于golang设置http response响应头与填坑记录的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-08-08
  • 一文深入探讨Go语言中的if-else语句

    一文深入探讨Go语言中的if-else语句

    在Go语言中,条件语句的使用方式相对简洁明了,所以本文将探讨一下如何在Go程序中有效地进行条件判断和逻辑控制,感兴趣的小伙伴可以了解下
    2023-08-08
  • 详解MongoDB Go Driver如何记录日志

    详解MongoDB Go Driver如何记录日志

    这篇文章主要为大家介绍了MongoDB Go Driver如何记录日志详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-08-08
  • golang-gin-mgo高并发服务器搭建教程

    golang-gin-mgo高并发服务器搭建教程

    这篇文章主要介绍了golang-gin-mgo高并发服务器搭建教程,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12

最新评论