专栏首页吴亲强的深夜食堂无限缓冲的channel(2)
原创

无限缓冲的channel(2)

chanx

上篇文章我们提到,当我们创建一个有缓冲的通道并指定了容量,那么在这个通道的生命周期内,我们将再也无法改变它的容量。 由此引发了关于无限缓存的 channel 话题讨论。 我们分析了一个实现无限缓冲的代码。 最后,我们也提到了它还可以继续优化的点。

鸟窝的 chanx 正是基于此方案改造而成的,我们来看看他俩的不同之处。

上篇文章说过,所谓的无限缓冲,无非是借助一个中间层的数据结构,暂存临时数据。

chanx 中,结构是这样的:

type UnboundedChan struct {
	In     chan<- T    // channel for write
	Out    <-chan T    // channel for read
	buffer *RingBuffer // buffer
}

inout 的职责在上篇文章已经说明,这里的 buffer 就是我们所谓的中间临时存储层。其中的 RingBuffer 结构我们后面再说。

func NewUnboundedChan(initCapacity int) UnboundedChan {
	return NewUnboundedChanSize(initCapacity, initCapacity, initCapacity)
}

func NewUnboundedChanSize(initInCapacity, initOutCapacity, initBufCapacity int) UnboundedChan {
	in := make(chan T, initInCapacity)
	out := make(chan T, initOutCapacity)
	ch := UnboundedChan{In: in, Out: out, buffer: NewRingBuffer(initBufCapacity)}

	go process(in, out, ch)

	return ch
}

它提供了两个初始化 UnboundedChan 的方法,从代码中我们可以明显的看出,NewUnboundedChanSize 可以给每个属性自定义自己的容量大小。仅此而已。

chanx 中 关于 inout 都是带缓冲的通道,而上篇文章中的 inout 都是无缓冲的通道。 这和他们对数据的流转处理有很大关系。

我们接下去看 process(in,out,ch) 最核心的方法。

这时候,我们再放上一篇核心代码。

可以很明显他们看出它俩的区别。

上篇从 in 通道读数据会先 appendbuffer,然后从 buffer 中取数据写入 out 通道。 而 chanxin 通道取出数据先尝试写入 out(没有中间商赚差价?),只有在 out 已经满的情况下,才塞入到 buffer

chanx 还有一段小细节代码。

能走到这里,一定是因为 out 通道满了。我们把值追加到 buffer 的同时,需要尝试把 buffer 中的数据写入 out 。 此时 in 通道也许还在持续的写入数据, 为了避免 in 通道塞满,阻塞业务写入,我们同时需要尝试从 in 通道中读数据追加到 buffer

buffer

上篇文章我提到了关于 buffer 优化的点。

chanx 是如何优化的?

// type T interface{}
type RingBuffer struct {
	buf         []T 
	initialSize int
	size        int
	r           int // read pointer
	w           int // write pointer
}

这是 buffer 的结构,其中

  • buf 具体存储数据的结构。
  • initialSize 初始化化 buf 的长度
  • size 当前 buf 的长度
  • r 当前读数据位置
  • w 当前写入数据位置

buffer 本质上就是一个环形的队列,目的是达到资源的复用。 并且当 buffer 满时,提供自动扩容的功能。

我们来看具体把数据写入 buffer 的源码。

接着看扩容。

这段代码唯一难理解的就是数据迁移了。这里的数据迁移目的是为了保证先入先出的原则。

可能加了注释有些人也无法理解,那么就再加一个草率图。

假设我们 buffer 的长度是 8。 当前读和写的 index 都是5。说明 buffer 满了,触发自动扩容规则,进行数据迁移。

那么迁移的过程就是下图这样的。

还有,当 buffer 为空并且当前的 size 比初始化 size 还大,那么可以考虑重置 buffer 了。

//if ch.buffer.IsEmpty() && ch.buffer.size > ch.buffer.initialSize { 
//						ch.buffer.Reset()
//					}
func (r *RingBuffer) Reset() {
r.r = 0
r.w = 0
r.size = r.initialSize
r.buf = make([]T, r.initialSize)
}

剩下的代码,就没什么好说的了。

总结

继上篇文章后,这篇文章我们主要讲解了 chanx 是如何实现无限缓冲的 channel。 其中最重要的一个点在于 chanxbuffer 实现采用的是 ringbuffer,达到资源复用的同时还能自动扩容。

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 无限缓冲的channel(1)

    事情的起因是前几周看到鸟窝写了一篇关于实现无限缓冲 channel 的文章,当时忙着和小姐姐聊天没看,今天想起来了。

    吴亲库里
  • golang 无缓冲channel

    landv
  • 【Go 语言社区】golang channel 有缓冲 与 无缓冲 的重要区别

    golang channel 有缓冲 与 无缓冲 是有重要区别的 我之前天真的认为 有缓冲与无缓冲的区别 只是 无缓冲的 是 默认 缓冲 为1 的缓冲式 其实是...

    李海彬
  • C的全缓冲、行缓冲和无缓冲

    基于流的操作最终会调用read或者write函数进行I/O操作。为了使程序的运行效率最高,流对象通常会提供缓冲区,以减少调用系统I/O库函数的次数。

    Dabelv
  • CC++的全缓冲、行缓冲和无缓冲

    C/C++中,基于I/O流的操作最终会调用系统接口read()和write()完成I/O操作。为了使程序的运行效率最高,流对象通常会提供缓冲区,以减少调用系统I...

    Dabelv
  • Java.NIO编程一览笔录

    Java标准IO 与 Java NIO 的简单差异示意: Java标准IO Java NIO API调用 简单 复杂 底层实现 面向流(str...

    斯武丶风晴
  • 抛砖引玉NIO

    在软件系统中,由于I/O的速度远比内存速度慢,所以I/O很容易成为系统的瓶颈。New I/O的简称,与旧式基于流的I/O相对。拥有如下特性:

    三哥
  • Coroutine(协程)(三)

    一个 Channel 是一个和 BlockingQueue 非常相似的概念。其中一个不同是它代替了阻塞的 put 操作并提供了挂起的 send,还替代了阻塞的 ...

    提莫队长
  • Netty4学习笔记 --- Netty入门

    互联网行业: 在分布式系统中,各个节点之间需要远程服务调用,高性能的 RPC 框架必不可少,Netty 作为异步高性能的通信框架,往往作为基础通信组件被这些 ...

    挽风
  • Java NIO之Java中的IO分类

    前面两篇文章(Java NIO之理解I/O模型(一)、Java NIO之理解I/O模型(二))介绍了,IO的机制,以及几种IO模型的内容,还有涉及到的设计模式。...

    纪莫
  • Netty系列| Netty创始人告诉你为什么选择NIO

    NIO模型 同步非阻塞 NIO有同步阻塞和同步非阻塞两种模式,一般讲的是同步非阻塞,服务器实现模式为一个请求一个线程,但客户端发送的连接请求都会注册到多路复用器...

    狼王编程
  • 走进Golang之Channel的使用

    相信写过 Go 的同学都知道这句名言,可以说 channel 就是后边这句话的具体实现。我们来看一下到底 channel 是什么?

    大愚
  • 了解NIO和BIO

    1.linux系统中一切皆文件当有文件 当有一个请求过来的時候就通过3次握手就会和内核创建连接关系,此时Tomcat中的启动的的端口监控就会检测到内核中的文件...

    居士
  • 你知道IO与NIO有什么区别吗?

    阻塞与非阻塞是描述进程在访问某个资源时,数据是否准备就绪的的一种处理方式。当数据没有准备就绪时:

    Java深度编程
  • Java IO与NIO

    七 Java NIO AsynchronousFileChannel异步文件通

    后端码匠
  • Netty中的这些知识点,你需要知道!

    Channel是一个接口,而且是一个很大的接口,我们称之为“大而全”,囊括了server端及client端接口所需要的接口。

    WindWant
  • 深度解密Go语言之channel

    大家好啊!“深度解密 Go 语言”系列好久未见,我们今天讲 channel,预祝阅读愉快!在开始正文之前,我们先说些题外话。

    梦醒人间
  • Netty一文深入

    通过2个位置指针来协助缓冲区的读写,读使用 readerIndex,写使用 writerIndex。

    趣学程序-shaofeer
  • BIO、NIO、AIO原理及总结

    同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销。

    用户5325874

扫码关注云+社区

领取腾讯云代金券