在现代的多核CPU的架构下,基本都会使用并发编程来提高程序的执行速度,在服务端编程,并发编程更是必不可少的。

一、并发控制的思路

在并发的运行模式下,当多个线程或者协程需要访问同一个资源时,为了保证数据的安全,必需要对数据的访问进行控制,即并发控制。并发控制一般有两种思路:

1)通过共享内存的方式进行通信

为了解决线程冲突地访问数据Data,我们需要限制同一时间可以有哪些线程可以访问数据,在实际的开发中,一般是根据具体的业务需求,使用互斥锁、读写锁等来控制的。目前很多的主流编程语言,多个线程传递数据的方式一般都是共享内存。

2)通过通信的方式共享内存

当多个协程(或者线程)需要访问数据Data时,多个协程并不会直接访问数据Data,而是通过Channel来传递数据,协程A把处理好的数据发送到Channel中,协程B再从Channel中接收数据并继续处理。协程A和协程B两者能够独立运行且不存在直接关联,但是能通过 Channel 间接完成通信。

Go语言虽然也提供各种锁的机制,但是同时也提供了Channel这种基于通信的并发控制。个人认为,相对于各种锁的机制,Channel的并发机制更加简单和易于使用。

二、Channel的创建

下面以我们在使用Channel的实际过程中,创建Channel来说明Channel的内部结构及原理。为了方便理解,我把Channel在Go runtime中的结构用图的形式表达了出来,并附上相关的注释,相应的源代码为:runtime/chan.go

type 

1)无Buffer Channel

由于是无Buffer Channel,所以buf指向了自己,dataqsiz为0。由于需要保证并发的安全,整个Channel的数据需要在lock的保障下进行读写。

注:sudog是对Goroutine的封装,表示一个等待状态的Goroutine。在创建一个Channel时,recvq和sendq都是空list,这里为了表达运行时的状态,对其进行了赋值,下面的创建方式的示意图也如此。

2)带Buffer Channel(数据类型为非指针)

上图是带Buffer Channel,且Channel的数据类型为int,不是指针类型。可以看到dataqsiz为10,buf指针一块10*sizeof(int)大小的内存,且当前队列是没有数据元素的(qcount为0)。

有意思的是,buf指向的内存,跟hchan结构是一次性分配的,即在内存上buf指向的内存跟hchan是连续的,这个不禁让我联想到C语言中的零数组,作用应该是减少内存分配和回收的次数、减少内存碎片。

3)带Buffer Channel(数据类型为指针)

上图是带Buffer Channel,且Channel的数据类型为*T,是指针类型。可以看到dataqsiz为10,buf指针一块10*sizeof(T)大小的内存,该内存与hchan是不连续的,是单独的一块内存,且当前队列是没有数据元素的(qcount为0)。

4)Channel创建的源代码

func 

三、发送数据(ch <- data)

1)无Buffer的Channel:

  1. 从recvq中,选择第一个G,并把数据赋值到该G相应的变量中,然后把该G从recvq中移除,并设置为可运行状态,供调度器进行调度;
  2. 若没有等待读的G,即recvq为空,则数据写入的G会被阻塞,被加入到sendq中等待

2)带Buffer的Channel:

  1. 若recvq不为空,选择第一个G,并把数据赋值到相应的变量存储中,然后把该G从recvq中移除,并设置为可运行状态,供调度器进行调度;
  2. 若recvq为空,且循环队列buf可写,把数据写buf中
  3. 若recvq为空,且循环队列buf已满,则数据写入的G会被阻塞,被加入到sendq中等待

其实现代码及解释如下:

func 

四、接收数据

在Go中,接收数据有两种方式,两种方式的不同之处是,第二种会返回获取数据是否成功,但是两种方式在Go中的处理是一致的,最后者是调用 chanrecv来实现

data 

1)无Buffer的Channel:

  1. 从sendq中,选择第一个G,并把数据赋值到该G相应的变量中,然后把该G从sendq中移除,并设置为可运行状态,供调度器进行调度;
  2. 若没有等待写的G,即sendq为空,则数据读取的G会被阻塞,被加入到recvq中等待

2)带Buffer的Channel:

  1. 若sendq不为空,选择第一个G,并把数据赋值到该G相应的变量中,然后把该G从sendq中移除,并设置为可运行状态,供调度器进行调度;
  2. 若sendq为空,且循环队列buf不为空,则从buf中读取数据
  3. 若sendq为空,且循环队列buf也为空,则数据读取的G会被阻塞,被加入到recvq中等待

数据接收的代码实现与数据发送在实现上差不多,不再分析

五、关闭Channel

关闭Channel,主要工作就是将 recvq 和 sendq 两个队列中的Goroutine加入到 Goroutine 列表 gList 中,标记为可执行,让调度器调试,并清除所有 sudog 上未被处理的元素。

关闭Channel后,数据写入会Panic,如果是带Buffer的Channel,Channel内的数据仍可读。Channel关闭后,hchan的closed为1,所有的读操作都会立即返回。

可以利用关闭Channel所有的读操作会立即返回这一特点,实现一个类似于广播的功能。

// 若channel无数据可读,ok为false;

关闭Channel的处理实现及解释如下:

func 

六、总结

Channel 为Go语言的高并发处理提供了强有力的支持,使用了不要通过共享内存的方式进行通信,而是应该通过通信的方式共享内存的设计模式,希望本文能对各位理解Channel提供一点帮助。

循环buffer的实现_Go并发编程-Channel的设计实现相关推荐

  1. go语言 rlock() defer runlock()_Go并发编程之美-读写锁

    一.前言 go语言类似Java JUC包也提供了一些列用于多线程之间进行同步的措施,比如低级的同步措施有 锁.CAS.原子变量操作类.相比Java来说go提供了独特的基于通道的同步措施.本节我们先来看 ...

  2. 【并发编程】如何用 Channel 解决并发问题?

    博主介绍: – 我是了 凡 微信公众号[了凡银河系]期待你的关注.未来大家一起加油啊~ 前言 什么是Channel? 在Go语言基础中应该就学过Channel,那个时候应该都认为只是一个基础类型,是一 ...

  3. GO并发编程基础- 如何使用channel

    一.channel 入门 1. 什么是 channel 通道,顾名思义,使用来传递数据信号的,和队列类似,有发送端和接收端,也是先进先出的数据结构,通常是在不同的 goroutine 做通信处理,结合 ...

  4. golang并发编程goroutine+channel(一)

    go语言的设计初衷除了在不影响程序性能的情况下减少复杂度,另一个目的是在当今互联网大量运算下,如何让程序的并发性能和代码可读性达到极致.go语言的并发关键词 "go" go dos ...

  5. 并发编程之循环屏障CyclicBarrier

    文章目录 前言 什么是CyclicBarrier CyclicBarrier原理 CyclicBarrier VS CountDownLatch CountDownLatch图示: CyclicBar ...

  6. 唤醒手腕 Go 语言 并发编程 (goroutine、channel)详细教程(更新中)

    线程.协程基本概念 协程是单线程下的并发,又称微线程,纤程.它是实现多任务的另一种方式,只不过是比线程更小的执行单元.因为它自带CPU的上下文,这样只要在合适的时机,我们可以把一个协程切换到另一个协程 ...

  7. Java 并发编程解析 | 如何正确理解Java领域中的多线程模型,主要用来解决什么问题?

    苍穹之边,浩瀚之挚,眰恦之美: 悟心悟性,善始善终,惟善惟道! -- 朝槿<朝槿兮年说> 写在开头 我国宋代禅宗大师青原行思在<三重境界>中有这样一句话:" 参禅之初 ...

  8. Java 并发编程 常见面试总结

    目录 一. Socket流阻塞 二.  wait和notify 三. 线程实现的两种方式 四. synchronized同步代码块示例 五. ReentrantLock的方法示例 六. Lock和sy ...

  9. Java 并发编程解析 | 如何正确理解Java领域中的内存模型,主要是解决了什么问题?

    写在开头 这些年,随着CPU.内存.I/O 设备都在不断迭代,不断朝着更快的方向努力.在这个快速发展的过程中,有一个核心矛盾一直存在,就是这三者的速度差异.CPU 和内存的速度差异可以形象地描述为:C ...

  10. [Professor麦]并发编程就该这么学(长文预警)

    二轮复盘并发编程!!文末有一些我学习并发编程的感受,不知道怎么入手的可以看看,正所谓传道授业解惑也,传递怎么学比知识更重要!欢迎在评论区和我交流讨论 什么是线程安全 当多个线程同时访问一个对象时,如果 ...

最新文章

  1. 数据快传对于企业的重要性!
  2. Golang 编程 — Go Micro 微服务框架
  3. 数据结构实验之图论九:最小生成树(Prim/Kruskal)
  4. win7计算机内存占用高,WIN7系统电脑内存占用高的解决办法有哪些
  5. JDK12的五大重要新特性
  6. 「第五篇」全国电子设计竞赛-电源题设计方案总结
  7. java如何禁用usb_IT技巧分享59: 如何禁用USB端口以及光驱来保证数据不被泄露
  8. 240多个jQuey插件
  9. 网络编程之OSI七层协议略析
  10. Python智能对话机器人实现
  11. ip查看服务器信息,如何查看服务器的出口ip地址
  12. [人物]发明了自我的人--卡帕(外二篇)
  13. Excel中的Array Formula
  14. 微信第三方平台开发流程
  15. 【组织架构】中国铁路上海局集团有限公司
  16. 类似Confluence的软件有哪些
  17. Python实现手写体数字图片识别+GUI界面+画板数字识别
  18. LVGL笔记10--lv_cont容器
  19. e2e测试稳定如何保障
  20. STM8L051F3单片机竟然没有TIM1定时器,却有TIM2,3,4!!!害我调了一天没调出来

热门文章

  1. LinkedList遍历方式区别
  2. 【社交分享SDK】ShareSDK for Android 2.5.9已经公布
  3. 物联网来了,智能城市离我们还有多远?
  4. 异曲同工 WinForm和ASP.NET如何选?
  5. 【Spring】13、使用Spring 3的@value简化配置文件的读取
  6. jetty的安装,优化
  7. 哈哈哈,看着问题一个个解决,很有满足感哦
  8. 2008 DHCP中继器代理服务
  9. 未能加载文件或程序集Microsoft.ReportViewer.WebForms
  10. java材质_教程 - JAVA版材质包制作教程 | MineBBS 我的世界中文论坛