Go语言 | goroutine不只有基础的用法,还有这些你不知道的操作

Netty 各组件的原理分析(九) Channel、EventLoop(Group)和 ChannelFuture Netty 网络抽象的代表: Channel—Socket; EventLoop—控制流、多线程处理、并发; ChannelFuture—异步通知。 Channel 和 EventLoop 关系如图: Channel 接口 基本的 I/O 操作(bind()、connect()、read()和 write())依赖于底层网络传输所提供的原 语。在基于 Java 的网络编程中,其基本的.. 阅读详情

今天是golang专题第15篇文章,我们来继续聊聊channel的使用。

在我们的上篇文章当中我们简单介绍了golang当中channel的使用方法,channel是golang当中一个非常重要的设计,可以理解为生产消费者模式当中的队列。但channel和队列不一样的是,golang当中集成了一些其他的用法,使得我们的使用更加灵活,开发并发相关的功能更加简单。

select机制

我们来思考一个问题,假设我们的数据源有多个,也就是说我们可能会从多个入口获取数据,但是我们并不知道这些数据源当中哪个先把数据准备好。我们希望实现轮询这些channel,哪个数据准备好了就读取哪个,否则就阻塞等待,这个功能应该怎么办呢?

我们当然可以自己用循环来实现,但是这显然是不合理的,golang当中针对这个问题提供了专门的解决方法,它就是select关键字。

select机制并不是golang这门语言独创的,早在Unix时代就有了select机制。通过select函数监控一系列文件的句柄,一旦其中一个发生了改动,select函数就会返回。而golang当中的select则用来在channel当中进行选择,有点像是switch,写出来的代码大概是这个样子:

select {
    case <- chan1:
    case chan2 <- 1:
    default:
}

select后面跟多个case以及default,其中default并不是必须的case后面必须要接一个chan的操作,可以是从一个chan当中读入数据,也可以是向一个chan当中写入数据。如果所有的case都不成功,则会进入default语句当中,如果没有default语句,那么select会陷入阻塞。

一般情况下我们使用select是为了从多个数据源中获取数据,当多个chan同时有数据的时候,使用select可以让我们避免判断哪个数据源数据ready的问题。

range机制

我们之前在介绍slice遍历的时候曾经介绍过range机制,我们可以通过range来遍历一个数组或者是map。就像是这样:

arr := make([]int, 0)
for i := range arr {
    // do something
}

mp := make(map[string]int)

for k, v := range mp {
    // do something
}

很多时候我们会把这个用法当做是迭代器的迭代,就像是Java和Python中的那样。但实际上range机制的底层原理是chan,当我们使用range的时候,它表示会不断地从chan当中接受值,直到它关闭。

所以我们也可以这样遍历一个chan当中的数据:

ch := make(chan int)

for c := range ch {
    // do something
}

超时机制

有没有想过一个问题,channel的写入和写出都是阻塞的,也就是说如果是从chan当中读取数据,必须要上游已经传输了才可以读取到。同样,如果往没有缓冲区的chan写入数据也需要下游消费了才能写入成功。阻塞往往是有很大隐患的,如果处理不好很容易导致整个程序锁死。

我们需要设计机制来解决这个问题,比较好的方案就是设置定时器,如果超过一定的时间chan还没有响应成功的话,那么就人工停止程序。这一点说起来还有点麻烦的,比如我们要启动一个定时器,要手动终止goroutine,但是结合select机制其实并不难实现,我们来看代码。

timeout := make(chan bool)
go func() {
    time.Sleep(1e9)
    timeout <- true
}()


select {
	case <- ch:
    	// do something
	case <- timeout: 
    	// break
}

说白了很简单,也就是我们额外启动一个goroutine做休眠操作,当休眠结束之后也通过chan发送消息,这样如果我们select先接受到了timeout的信号就说明了程序已经超时了。当然这只是一个很简单的demo,实际使用的话需要考虑的情况可能还会更多。

channel传递

有没有想过一个问题,既然chan可以传输任何类型的数据,那么我们能不能用一个chan传输一个chan呢?

这样的操作是可以的,因为在有些场景当中相比于直接把数据传输给下游,我们传输读取数据的chan可能更加方便。有点授人以渔的意思,更加厉害的是我们可以结合函数式编程,把处理数据的函数一并传输给下游。这样下游读取到数据,并且用读取到的处理函数来处理,这样可以更加定制化,如果以后数据和处理方式都发生改动,也只需要在上游修改,可以更加解耦。

我们同样来看一个demo:

type MetaData struct {
    value interface{}
    handler func(interface{}) int
    downstream chan interface{}
}


func handle(queue chan *MetaData) {
    for data := range queue {
        data.downstream <- data.handler(data.value)
    }
}

这只是一个简单的案例,想要在实际应用当中真的使用上还需要定义大量的接口以及做很多设计。我们只需要知道有这么一种设计模式和用法就可以了。

单向channel

最后,我们来说说单向channel,也就是说我们指定channel是只读的或者是只写的。但其实这是一个伪命题,原因也很简单,如果只写数据没人读,或者是只读但是不能写,那么这个channel有什么用呢?只有有人读有人写才可以完成数据流通不是吗?

的确如此,所以这里所说的单向channel其实并不是真正意义上的单向,只是说我们为了规范,对使用方进行限制。比如说我们限定在消费函数当中不能写入,在生产函数当中不能消费。我们在通过函数传递chan的时候,可以通过加上限定让chan在函数当中变成单向的。

var ch chan <- float32 // 只写chan
var ch <- chan float32 // 只读chan

除此以外我们还可以把一个正常的chan转化成单向的chan:

var ch chan int
ch1 := <- chan int(ch)
ch2 := chan <- int(ch)

我们一般不在程序当中做这样的转化,而是用在函数参数当中,这也主要是为了起到规范的作用。

func Test(ch <- chan int) {
    for val := range ch {
        // do something
    }
}

今天的文章到这里就结束了,如果喜欢本文的话,请来一波素质三连,给我一点支持吧(关注、转发、点赞)。

原文链接,求个关注

在这里插入图片描述

Read book Netty in action(Chapter VII)--ChannelHandler和ChannelPipeline 这项关联是永久性的;另一种高级的用法是缓存到ChannelHandlerContext 的引用以供稍后使用,这可能会发生在任何的ChannelHandler 方法之外,甚至来自于同的线程。在ChannelInboundHandlerAdapter 和ChannelOutboundHandlerAdapter 中所提供的方法体调用了其相关联的ChannelHandlerContext 上的等效方法,从而将事件转发到了ChannelPipeline 中的下一个ChannelHandler 中。 阅读详情

相关推荐

Golang学习之旅】Go 语言基础语法概览

✅ 适合人群: Golang 新手 | 后端开发者 | 面试求职者✅ 文章亮点: 语法详解 + 实战代码 + 高质量示例✅ 目标: 10 分钟快速掌握 Golang 基础语法,助你高效写项目!Go 语言Golang)是Google开发的一门静态强类型并发支持强语法简洁的编程语言,广泛应用于后端开发云计算微服务区块链等领域。🔹 Go 语言的 4 大核心特点:✅ 语法简洁(减少代码冗余,开发高效)✅ 高性能(比 Python 快,比 Java 简单)

程序员林北北的博客 1352

GolangGo语言中如何通过Goroutine快速实现高并发

并发编程是指在一个程序中同时运行多个任务,这些任务可以独立地执行,也可以相互协作。并发编程可以提高程序的执行效率,特别是在处理大量I/O操作或计算密集型任务时。 在Go语言中,并发编程主要通过goroutine和channel来实现。 GoroutineGo语言独有的并发执行单元,它允许函数或方法并发执行,而无需手动管理线程。 Channel是Go语言中进行goroutine间通信和同步的主要机制。

景天科技苑 5万+

Netty核心组件之一

Netty核心组件之一 1 Netty核心组件 1.Channel IO操作的一种连接,实体与实体之间的连接,实体可以是硬件,文件,网络套接字,或者是程序组件Channel 是 Netty 网络操作抽象类,它除了包括基本的 I/O 操作,如 bind、connect、read、write 之外,还包括了 Netty 框架相关的一些功能,如获取该 Channe l的 EventLoop。(这个 Channel 我们可以理解为 Socket 连接,它负责基本的 IO 操作,例如:bind(),connect()

weixin_40055163的博客 1124

Netty ChannelInactive 断链场景分析

记录一下ChannelInactive的触发场景

qq_37913898的博客 1万+

netty主动断开连接_使用Netty实现 IM 聊天贼简单,看懂就锤爆哪吒的狗头~(结局)...

5. 断开重连Netty 客户端需要实现断开重连机制,解决各种情况下的断开情况。例如说:Netty 客户端启动时,Netty 服务端处于挂掉,导致无法连接上。在运行过程中,Netty 服务端挂掉,导致连接被断开。任一一端网络抖动,导致连接异常断开。具体的代码实现比较简单,只需要在两个地方增加重连机制。Netty 客户端启动时,无法连接 Netty 服务端时,发起重连。Netty 客户端运行时,和 ...

weixin_39616367的博客 2432

java case用法_Go语言 | goroutine只有基础用法,还有这些你知道操作

今天是golang专题第15篇文章,我们来继续聊聊channel的使用。在我们的上篇文章当中我们简单介绍了golang当中channel的使用方法,channel是golang当中一个非常重要的设计,可以理解为生产消费者模式当中的队列。但channel和队列一样的是,golang当中集成了一些其他的用法,使得我们的使用更加灵活,开发并发相关的功能更加简单。select机制我们来思考一个问题,假设...

weixin_39963853的博客 137

channelinactive触发后关闭channel_Go语言 | goroutine只有基础用法,还有这些你知道操作...

点击上方蓝字,关注并星标,和我一起学技术。今天是golang专题第15篇文章,我们来继续聊聊channel的使用。在我们的上篇文章当中我们简单介绍了golang当中channel的使用方法,channel是golang当中一个非常重要的设计,可以理解为生产消费者模式当中的队列。但channel和队列一样的是,golang当中集成了一些其他的用法,使得我们的使用更加灵活,开发并发相关的功...

weixin_42134168的博客 138

Go基础Go语言Goroutine 和 Channel 的声明与使用

本文介绍了Go语言中的并发编程核心概念,包括Goroutine和Channel。Goroutine是轻量级的执行单元,由Go运行时调度,启动成本低,适合高并发场景。Channel用于goroutine间的通信,支持同步和异步操作。文章详细讲解了Goroutine的启动方式、调度特点以及Channel的声明和使用,并通过示例代码演示了它们的协同工作原理。此外,还提供了并发编程的实践建议,如避免数据竞争、合理使用Goroutine和优雅退出机制。

数据知道的博客 6482

Go语言中的goroutine

目录Go语言中的并发Go并发优势goroutinegoroutine定义线程和协程的区别创建goroutine Go语言中的并发 并发是指在同一段时间内,程序可以执行多个任务。   Go并发优势 Go语言最大的特点就是从语言层面支持并发,开发者用担心并发的底层逻辑、内存管理,只需要编写好自己的业务逻辑即可。Go语言也提供了十分强大的垃圾回收机制,开发者用担心创建的量如何销毁。   在其他语言中,编写并发程序往往需要使用其他的并发库才能实现。而在Go语言里,想要编写一个并发程序是非常容

whhcs的博客 7892

GolangGo语言编程思想(五):Goroutine

GolangGoroutine 进行学习,并复习了协程的概念。

YGGP的博客 1335

Golang Goroutine和线程的区别

Golang 通过复杂的协程操作来实现我们的并发需求,golang是用户线程与系统线程的对应关系是多对多,既能利用多核cpu资源,也能尽可能减少上下文切换成本,代价是go需要实现复杂的goroutine调度机制。 相比于N:1时所有用户线程对应1个系统线程,无法利用多核cpu;1:1时1个用户线程对应一个系统线程,上下文切换成本高。通过复杂的调度实现N:N时,即能利用多核cpu资源,也能尽可能减少上下文切换成本,成为Go语言最为人知的特点,天生支持高并发与高效。

猫轻王的博客 1143

吃透 Golang 基础Goroutine

基于《Go 语言圣经》对 Golang Goroutine 以及 Channel 的相关内容进行系统性的回顾。

YGGP的博客 1479

一文读懂什么是Go语言goroutine

线程,进程,协程goroutine,context,GMP,内存泄露,waitgroup使用

m0_66316879的博客 1593

Golang Goroutine

什么是 Goroutine goroutineGo 并行设计的核心。goroutine 说到底其实就是协程,它比线程更小,十几个 goroutine 可能体现在底层就是五六个线程,Go 语言内部帮你实现了这些 goroutine 之间的内存共享。 执行 goroutine 只需极少的栈内存(大概是4~5KB),当然会根据相应的数据伸缩。也正因为如此,可同时运行成千上万个并发任务。goroutine 比 thread 更易用、更高效、更轻便。 一般情况下,一个普通计算机跑几十个线程就有点负载过大了,但是

李培冠的博客 9707

Go语言goroutine详解

有很多人对 goroutine死很理解,goroutineGo语言中的轻量级线程实现,由 Go 运行时(runtime)管理。Go 程序会智能地将 goroutine 中的任务合理地分配给每个 CPU。 goroutine由来 在编写 Socket 网络程序时,需要提前准备一个线程池为每一个 Socket 的收发包分配一个线程。我们需要在线程数量和 CPU 数量间建立一个对应关系,以保证每个任务能及时地被分配到 CPU 上进行处理,同时避免多个任务频繁地在线程间切换执行而损失效率。 虽然,线程池为

zp17834994071的博客 1808

GoLang之线程、协程、goroutine、锁、原子操作

GoLang之线程、协程、goroutine、锁、原子操作

weixin_52690231的博客 2768

GO 语言Goroutine 原理解析

并发一个 CPU 上能同时执行多项任务,在很短时间内,CPU 来回切换任务执行(在某段很短时间内执行 程序 a,然后又迅速得切换到程序 b 去执行),有时间上的重叠(宏观上是同时的,微观仍是顺序执行),这样看起来多个任务像是同时执行,这就是并发。 并行当系统有多个 CPU 时,每个 CPU 同一时刻都运行任务,互抢占自己所在的 CPU 资源,同时进行, 称为并行。 进程CPU 在切换程...

reboot_xwa的博客 2758
上一篇: Pandas | Dataframe的merge操作,像数据库一样尽情join
下一篇: Python | 使用argparse解析命令行参数
TechFlow
博客等级 码龄7年 345粉丝 276原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值