无缓冲通道

是指在接收前没有能力保存任何值得通道。
这种类型的通道要求发送goroutine和接收goroutine同时准备好,才能完成发送和接收操作。如果两个goroutine没有同时准备好,通道会导致先执行发送或接收操作的goroutine阻塞等待。
这种对通道进行发送和接收的交互行为本身就是同步的,其中任意一个操作都无法离开另一个操作单独存在。

上图所示,如同接力赛。根据图编号观察①两个协程,创建好了通道②一个往通道里放,这时候两边阻塞④这时候另一个协程要接⑤另一个协程取出来,从①-⑤都是阻塞的,⑥才完成交接,才不会阻塞。

再比喻: 就是一个送信人去你家门口送信 ,你不在家 他不走,你一定要接下信,他才会走。

无缓冲channel创建

ch := make(chan int, )  //第二个参数为0,或者不写第二个参数

如果没有指定缓冲区容量,那么该通道就是同步的,因此会阻塞到发送者准备好发送和接收者准备好接收。

代码案例

package main

import (
"fmt"
"time"
) func main() {
//创建一个无缓存的channel
ch := make(chan int, ) //len(ch)缓冲区剩余数据个数, cap(ch)缓冲区大小,两者这里永远都是0
fmt.Printf("len(ch) = %d, cap(ch)= %d\n", len(ch), cap(ch)) //新建协程
go func() {
for i := ; i < ; i++ { //写三次
fmt.Printf("子协程:i = %d\n", i)
ch <- i //往chan写内容
fmt.Printf("len(ch) = %d, cap(ch)= %d\n", len(ch), cap(ch))
}
}() //延时2秒
time.Sleep( * time.Second) for i := ; i < ; i++ { //必须读三次
num := <-ch //读管道中内容,没有内容前,阻塞
fmt.Println("num = ", num)
} }
len(ch) = , cap(ch)=
子协程:i =
num =
len(ch) = , cap(ch)=
子协程:i =
len(ch) = , cap(ch)=
子协程:i =
num =
num =

流程分析

主协程执行到这里,阻塞两秒
//延时2秒
time.Sleep( * time.Second) //两秒钟时间子协程肯定把for循环执行完毕,但这里也会出现阻塞
//阻塞原因是:当执行到往通道写数据是无缓冲的,对方不读之前会阻塞。也就是,在主协程等着子协程写完,但是主协程还没到读的时候,这时候出现阻塞,等到主协程读完数据才会往下走。
可以执行观察一下程序的执行卡顿观察阻塞
for i := ; i < ; i++ { //写三次
fmt.Printf("子协程:i = %d\n", i)
ch <- i //往chan写内容
fmt.Printf("len(ch) = %d, cap(ch)= %d\n", len(ch), cap(ch))
}

打印结果分析:首先子协程for循环往管道里写入一个数据,紧接着主协程for循环出现阻塞,然后主协程for循环从管道读数据,读完了打印。主协程打印完,子协程for循环继续给管道数据,但也有可能主协程读完数据没来得及打印,子协程就把数据写入管道并打印完毕,因为两个是同时并行的。

有缓冲通道

指通道可以保存多个值。

如果给定了一个缓冲区容量,那么通道就是异步的,只要缓冲区有未使用空间用于发送数据,或还包含可以接收的数据,那么其通信就会无阻塞地进行

上图所示:

①右侧的goroutine正在从通道接收一个值。
②右侧的goroutine独立完成了接手值得动作,而左侧的goroutine正在发送一个新值到通道里。
③左侧的goroutine还在向通道发送新值,而右侧的goroutine正在从通道接收另一个值。这个步骤里的两个操作既不是同步,也不会互相阻塞。
④所有的发送和接收都完成,而通道里还有几个值,也有一些空间可以存储更多的值

有缓冲channel创建

ch := make(chan int, )  //容量是3

代码案例

package main

import (
"fmt"
"time"
) func main() {
//创建一个有缓存的channel
ch := make(chan int, ) //容量是3 //len(ch)缓冲区剩余数据个数, cap(ch)缓冲区大小
fmt.Printf("len(ch) = %d, cap(ch)= %d\n", len(ch), cap(ch)) //新建协程
go func() {
for i := ; i < ; i++ { //这里数据量大于管道容量,会出阻塞
ch <- i //往chan写内容,如果主协程没读的话,写满3个就会阻塞在此
fmt.Printf("子协程[%d]: len(ch) = %d, cap(ch)= %d\n", i, len(ch), cap(ch))
}
}() //延时
time.Sleep( * time.Second) for i := ; i < ; i++ { //这里数据量大于管道容量,会出阻塞
num := <-ch //读管道中内容,没有内容前,阻塞
fmt.Println("num = ", num)
} }
len(ch) = , cap(ch)=
子协程[]: len(ch) = , cap(ch)=
子协程[]: len(ch) = , cap(ch)=
子协程[]: len(ch) = , cap(ch)=
num =
num =
num =
num =
子协程[]: len(ch) = , cap(ch)=
子协程[]: len(ch) = , cap(ch)=
子协程[]: len(ch) = , cap(ch)=
子协程[]: len(ch) = , cap(ch)=
子协程[]: len(ch) = , cap(ch)=
num =
num =
num =
num =
num =
子协程[]: len(ch) = , cap(ch)=
子协程[]: len(ch) = , cap(ch)=
num =

总结一下有缓冲channel和无缓冲channel的特点与不同

无缓冲的与有缓冲channel有着重大差别,那就是一个是同步的 一个是非同步的。

比如
c1:=make(chan int) 无缓冲
c2:=make(chan int,) 有缓冲
c1<-

无缓冲: 不仅仅是向 c1 通道放 1,而是一直要等有别的携程 <-c1 接手了这个参数,那么c1<-1才会继续下去,要不然就一直阻塞着。
有缓冲: c2<-1 则不会阻塞,因为缓冲大小是1(其实是缓冲大小为0),只有当放第二个值的时候,第一个还没被人拿走,这时候才会阻塞。

Golang并发编程有缓冲通道和无缓冲通道(channel)的更多相关文章

  1. Golang - 并发编程

    目录 Golang - 并发编程 1. 并行和并发 2. go语言并发优势 3. goroutine是什么 4. 创建goroutine 5. runtime包 6. channel是什么 7. ch ...

  2. Golang并发编程——goroutine、channel、sync

    并发与并行 并发和并行是有区别的,并发不等于并行. 并发 两个或多个事件在同一时间不同时间间隔发生.对应在Go中,就是指多个 goroutine 在单个CPU上的交替运行. 并行 两个或者多个事件在同 ...

  3. golang并发编程

    golang并发编程 引子 golang提供了goroutine快速实现并发编程,在实际环境中,如果goroutine中的代码要消耗大量资源时(CPU.内存.带宽等),我们就需要对程序限速,以防止go ...

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

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

  5. Golang并发编程优势与核心goroutine及注意细节

    Go语言为并发编程而内置的上层API基于CSP(communication sequential processes,顺序通信进程)模型.这就意味着显式锁都是可以避免的,比如资源竞争,比如多个进程同时 ...

  6. Golang并发编程基础

    硬件 内存 作为并发编程一个基础硬件知识储备,首先要说的就是内存了,总的来说在绝大多数情况下把内存的并发增删改查模型搞清楚了其他的基本上也是异曲同工之妙. 内存芯片--即我们所知道的内存颗粒,是一堆M ...

  7. Go语言的通道(1)-无缓冲通道

    前言: 上文中我们采用了[原子函数]已经[共享锁]两种方式分别对多个goroutine进行了同步,但是在go语言中提供了另一种更好的方式,那就是使用通道(Channel). 一.通道是什么? 其实无论 ...

  8. golang并发编程的两种限速方法

    引子 golang提供了goroutine快速实现并发编程,在实际环境中,如果goroutine中的代码要消耗大量资源时(CPU.内存.带宽等),我们就需要对程序限速,以防止goroutine将资源耗 ...

  9. Golang并发编程进程通信channel了解及简单使用

    概念及作用 channel是一个数据类型,用于实现同步,用于两个协程之间交换数据.goroutine奉行通过通信来共享内存,而不是共享内存来通信.引用类型channel是CSP模式的具体实现,用于多个 ...

随机推荐

  1. 模仿以太坊 ERC20 规范的 Hyperledger Fabric 实现 Token 通证

    合约: package main /* -------------------------------------------------- Author: netkiller <netkill ...

  2. (转)java调用python脚本

    这篇博客旨在吐血分享今天遇到的java调用python脚本遇到的坑,折腾了3个多小时终于可以跑通了,代码超级短,但网上的好多资料都是抄来抄去的,很少有能够直接跑通的,尤其是针对你的python文件中用 ...

  3. SaltStack 模块

    包安装模块.文件维护模块.服务模块 zabbix_agent: pkg.installed: - name: zabbix22-agent file.managed: - name: /etc/zab ...

  4. vuex中的dispatch和commit

    dispatch:含有异步操作,eg:向后台提交数据,写法: this.$store.dispatch('mutations方法名',值) commit:同步操作,写法:this.$store.com ...

  5. 如何解决gerrit代码冲突

    日常开发中,我们存在多人开发和同一个人提交多次记录的情况,这就避免不了代码冲突的情况出现. 下面介绍几种gerrit提交失败的现象,后续会根据大家遇到的情况,持续更新. 注意:出现合入不了,显示“ca ...

  6. 《全栈性能Jmeter》-5JMeter负载与监听

  7. [LeetCode] 693. Binary Number with Alternating Bits_Easy

    Given a positive integer, check whether it has alternating bits: namely, if two adjacent bits will a ...

  8. 初尝Web API《转》

    HTTP 并不是只能用在网页中.它其实还是一个强大的平台,可以用来生成一些API,暴露服务和数据.HTTP很简单灵活,还非常普及.几乎所有你能想到的平台都有HTTP库,所以HTTP服务可以囊括很大范围 ...

  9. js判断当前页面是否有父页面,页面部分跳转解决办法,子页面跳转父页面不跳转解决 (原)

    //如果当前页面存在父页面,则当前页面的父页面重新加载(即子页面父页面连带跳转) if(top.location!=self.location){         window.parent.loca ...

  10. C# dataGridView 如何选中整行?

    this.dataGridView1.SelectionMode =DataGridViewSelectionMode.FullRowSelect; dataGridView1即你的dataGridV ...