无缓冲通道

是指在接收前没有能力保存任何值得通道。
这种类型的通道要求发送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. Java通过POI生成Excel

    import java.io.FileOutputStream; import java.text.SimpleDateFormat; import java.util.ArrayList; impo ...

  2. MySQL 基础 简单操作

    一.数据库基础 什么是数据库 数据库:保存有组织的数据的容器(通常是一个文件或一组文件). 表:是一种结构化的文件,可以用来存储数据(类似Excel表).数据库就是由成千上万个表组成. 什么事SQL ...

  3. fasttext学习笔记

    When to use FastText? The main principle behind fastText is that the morphological structure of a wo ...

  4. heapy() :python自带的堆排序

    堆是一个二叉树,其中每个父节点的值都小于或等于其所有子节点的值.整个堆的最小元素总是位于二叉树的根节点.python的heapq模块提供了对堆的支持. 堆数据结构最重要的特征是heap[0]永远是最小 ...

  5. Hinge Loss、交叉熵损失、平方损失、指数损失、对数损失、0-1损失、绝对值损失

    损失函数(Loss function)是用来估量你模型的预测值 f(x) 与真实值 Y 的不一致程度,它是一个非负实值函数,通常用 L(Y,f(x)) 来表示.损失函数越小,模型的鲁棒性就越好. 损失 ...

  6. xcode如何支持8.0以下

    1. shell打开 open  /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSuppor ...

  7. numpy&pandas笔记

    1.基础属性: array = np.array([[1,2,3],[2,3,4]]) #列表转化为矩阵 print('number of dim:',array.ndim) # 维度 # numbe ...

  8. bat cmd 获取管理员权限

    @ echo off % % ver|find "5.">nul&&goto :Admin mshta vbscript:createobject()(win ...

  9. LeetCode104.二叉树最大深度

    给定一个二叉树,找出其最大深度. 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数. 说明: 叶子节点是指没有子节点的节点. 示例:给定二叉树 [3,9,20,null,null,15,7], ...

  10. Jenkins实现自动化部署

    首先登录到服务器,目前我所使用的program-DEV环境所在服务器为:IP     登录密码为:密码 1.基础配置 检查所有环境变量: $ env 2.下载安装包安装 JDK:由于本机器已经安装,这 ...