[管道]

分为 有缓冲和无缓冲两种

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

比如:

c1:=make(chan int)         无缓冲
c2:=make(chan int,1) 有缓冲

    例如:c1<-1

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

无缓冲:

1)接受者与发送者必然存在于两个协程, 否则会造成互相等待 死锁的情况

2) 对于关闭的Channel,无法再进行写入, 但是可以读取,并继续向下执行

顺序执行多协程:

    var ch1 = make(chan int)

    var stopFlag = make(chan bool)
// 保证两个协程顺序执行 go func() {
fmt.Println("g1")
time.Sleep(3 * time.Second)
ch1 <- 1
}() go func() {
<-ch1
fmt.Println("g2")
time.Sleep(1 * time.Second)
stopFlag <- true
}() <-stopFlag
fmt.Println("over")

使用同步原语

func tt4() {

    // 2个任务顺序执行
runChan := make(chan struct{}) // 无缓冲Channel
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
println("task1 ...")
time.Sleep(time.Second * 2)
println("task1 !!!")
runChan <- struct{}{}
}() go func() {
defer wg.Done()
<-runChan
println("task2 ...")
time.Sleep(time.Second * 1)
println("task2 !!!")
}()
wg.Wait()
fmt.Println("main task finished!!!")
}

有缓冲:

1.允许同一个协程中, 顺序执行, 但是保证发送的数量与接收的数量相等,否则会造成死锁

[协程]

实例:

go funct(){
...
}()

实现主进程Hold的方式

1)使用无缓冲的channel, 异常情况下实现终止操作,例如gin框架保证服务的异常中断之后的处理

2)使用select{} 即可

chan与range结合使用:

v, ok := <- ch中的ok仅表示是否成功读取了channel中的数据,并不代表channel的关闭状态。

for循环的for range形式可用于从通道接收值,直到它关闭为止。

非缓冲channel读取和写入都会阻塞直至另一个goroutine往channel中写入和读取数据,带缓冲的channel只有缓冲区满了,写入会阻塞,缓冲区没有数据读取会阻塞。

关闭通道

发送者可以通过关闭信道,来通知接收方不会有更多的数据被发送到channel上。

 
go

 
close(ch)

接收者可以在接收来自通道的数据时使用额外的变量来检查是否成功读取了数据

语法结构:

 
go

 
v, ok := <- ch

类似map操作,存储key,value键值对

v,ok := map[key] //根据key从map中获取value,如果key存在, v就是对应的数据,如果key不存在,v是默认值

在上面的语句中,如果ok的值是true,表示成功的从通道中读取了一个数据value。如果ok是false,这意味读取的值是通道类型的零值。

例如,如果通道是一个int通道,那么零值将为0。

示例代码:

 
go

 
func main()  {
ch1 := make(chan int)
go sendData(ch1)
/*
子goroutine,写出数据10个
每写一个,阻塞一次,主程序读取一次,解除阻塞 主goroutine:循环读
每次读取一个,堵塞一次,子程序,写出一个,解除阻塞 发送发,关闭通道的--->接收方,接收到的数据是该类型的零值,以及false
*/
//主程序中获取通道的数据
for{
time.Sleep(1*time.Second)
v, ok := <- ch1 //其他goroutine,显示的调用close方法关闭通道。
if !ok{
fmt.Println("已经读取了所有的数据,", v, ok)
break
}
fmt.Println("取出数据:",v, ok)
} fmt.Println("main...over....")
}
func sendData(ch1 chan int) {
// 发送方:10条数据
for i:=0;i<10 ;i++ {
ch1 <- i//将i写入通道中
}
close(ch1) //将ch1通道关闭了。
}

在上面的程序中,send Goroutine将0到9写入chl通道,然后关闭通道。主函数里有一个无限循环。它检查通道是否在发送数据后,使用变量ok关闭。如果ok是假的,则意味着通道关闭,因此循环结束。还可以打印接收到的值和ok的值。

for range 循环 channel

我们可以循环从通道上获取数据,直到通道关闭。for循环的for range形式可用于从通道接收值,直到它关闭为止。

使用range循环,示例代码:

 
go

 
func main()  {
ch1 :=make(chan int)
go sendData(ch1)
// for循环的for range形式可用于从通道接收值,直到它关闭为止。
for v := range ch1{
fmt.Println("读取数据:",v)
}
fmt.Println("main..over.....")
}
func sendData(ch1 chan int) {
for i:=0;i<10 ; i++ {
time.Sleep(1*time.Second)
ch1 <- i
}
close(ch1)//通知对方,通道关闭
}

非缓冲通道

前面讲的所有通道基本上都没有缓冲。发送和接收到一个未缓冲的通道是阻塞的。

一次发送操作对应一次接收操作,对于一个goroutine来讲,它的一次发送,在另一个goroutine接收之前都是阻塞的。同样的,对于接收来讲,在另一个goroutine发送之前,它也是阻塞的。

缓冲通道

缓冲通道就是指一个通道,带有一个缓冲区。发送到一个缓冲通道只有在缓冲区满时才被阻塞。类似地,从缓冲通道接收的信息只有在缓冲区为空时才会被阻塞。

可以通过将额外的容量参数传递给make函数来创建缓冲通道,该函数指定缓冲区的大小。

语法:

 
go

 
ch := make(chan type, capacity)

上述语法的容量应该大于0,以便通道具有缓冲区。默认情况下,无缓冲通道的容量为0,因此在之前创建通道时省略了容量参数。

示例代码

以下的代码中,channel是带有缓冲区的。

 
go

 
func main() {
/*
非缓存通道:make(chan T)
缓存通道:make(chan T ,size)
缓存通道,理解为是队列: 非缓存,发送还是接受,都是阻塞的
缓存通道,缓存区的数据满了,才会阻塞状态。。 */
ch1 := make(chan int) //非缓存的通道
fmt.Println(len(ch1), cap(ch1)) //0 0
//ch1 <- 100//阻塞的,需要其他的goroutine解除阻塞,否则deadlock ch2 := make(chan int, 5) //缓存的通道,缓存区大小是5
fmt.Println(len(ch2), cap(ch2)) //0 5
ch2 <- 100 //
fmt.Println(len(ch2), cap(ch2)) //1 5 //ch2 <- 200
//ch2 <- 300
//ch2 <- 400
//ch2 <- 500
//ch2 <- 600
fmt.Println("--------------")
ch3 := make(chan string, 4)
go sendData3(ch3)
for {
time.Sleep(1*time.Second)
v, ok := <-ch3
if !ok {
fmt.Println("读完了,,", ok)
break
}
fmt.Println("\t读取的数据是:", v)
} fmt.Println("main...over...")
} func sendData3(ch3 chan string) {
for i := 0; i < 10; i++ {
ch3 <- "数据" + strconv.Itoa(i)
fmt.Println("子goroutine,写出第", i, "个数据")
}
close(ch3)
}
 

更多使用示例:

https://max2d.com/archives/947

golang之协程+chan通道的更多相关文章

  1. [Golang]-5 协程、通道及其缓冲、同步、方向和选择器

    目录 协程 通道 通道缓冲 通道同步 通道方向 通道选择器 协程 Go 协程 在执行上来说是轻量级的线程. 代码演示 import ( "fmt" "time" ...

  2. 『GoLang』协程与通道

    作为一门 21 世纪的语言,Go 原生支持应用之间的通信(网络,客户端和服务端,分布式计算)和程序的并发.程序可以在不同的处理器和计算机上同时执行不同的代码段.Go 语言为构建并发程序的基本代码块是 ...

  3. goroutine 分析 协程的调度和执行顺序 并发写 run in the same address space 内存地址 闭包 存在两种并发 确定性 非确定性的 Go 的协程和通道理所当然的支持确定性的并发方式(

    package main import ( "fmt" "runtime" "sync" ) const N = 26 func main( ...

  4. GoLang之协程

    GoLang之协程 目前,WebServer几种主流的并发模型: 多线程,每个线程一次处理一个请求,在当前请求处理完成之前不会接收其它请求:但在高并发环境下,多线程的开销比较大: 基于回调的异步IO, ...

  5. Golang 之协程详解

    转自:https://www.cnblogs.com/liang1101/p/7285955.html 一.Golang 线程和协程的区别 备注:需要区分进程.线程(内核级线程).协程(用户级线程)三 ...

  6. 在C++中使用golang的协程

    开源项目cpp_features提供了一个仿golang协程的stackful协程库. 可以在c++中使用golang的协程,大概语法是这样的: #include <iostream> v ...

  7. GoLang 的协程调度和 GMP 模型

    GoLang 的协程调度和 GMP 模型 GoLang 是怎么启动的 关于 GoLang 的汇编语言,请查阅 参考文献[1] 和 参考文献[2] 编写一个简单的 GoLang 程序 main.go, ...

  8. Golang协程与通道整理

    协程goroutine        不由OS调度,而是用户层自行释放CPU,从而在执行体之间切换.Go在底层进行协助实现      涉及系统调用的地方由Go标准库协助释放CPU      总之,不通 ...

  9. golang:协程安全

    多路复用 Go语言中提供了一个关键字select,通过select可以监听channel上的数据流动.select的用法与switch语法类似,由select开始一个新的选择块,每个选择条件由case ...

  10. Golang 的 协程调度机制 与 GOMAXPROCS 性能调优

    作者:林冠宏 / 指尖下的幽灵 掘金:https://juejin.im/user/587f0dfe128fe100570ce2d8 博客:http://www.cnblogs.com/linguan ...

随机推荐

  1. AWS Cloud Practioner 官方课程笔记 - Part 3

    AWS Security 方案和功能 Amazon Inspector AWS Shield Price and Support Free Tier: Always Free, 12-month fr ...

  2. 8.30域横向-PTH&PTK&PTT票据传递

    知识点 Kerberos协议具体工作方法,在域中: 客户机将明文密码进行NTLM哈希,然后和时间戳一起加密(使用krbtgt密码hash作为密钥),发送给kdc(域控),kdc对用户进行检测,成功之后 ...

  3. Angular 16+ 高级教程 – 谈谈 ASP.NET Core & Angular & React 在业务开发上各自的优势和体验

    前言 日常, 我的开发都围绕着 ASP.NET Core 和 Angular. 这篇想聊聊它们各自的特点和解决问题的方式. 以及最重要的, 我们该在什么时候采用何种方案更为妥当. 浅谈项目分类 我一般 ...

  4. 【学习笔记】状压DP

    状态压缩DP 对于一个集合,他一有\(2^n\)个子集,而状态压缩就是枚举这些子集,每一个状态就是一个由\(01\)构成的集合,如果为\(0\)就表示不选当前的元素,否则就表示选.因为状态压缩将每一个 ...

  5. 系统编程-进程-close-on-exec机制

    我的相关博文: 系统编程-进程-exec系列函数超级详解(带各种实操代码) 一般我们会调用exec执行另一个程序,此时会用全新的程序替换子进程的正文,数据,堆和栈等. 此时保存文件描述符的变量当然也不 ...

  6. 使用hexo进行github博客搭建

    1.你必须建一个存储库,这个存储库要和你的github名称一致(不然就会404),如下图: 2.如果是window配置,需要安装git和node.js 3.下载hexo npm config set ...

  7. Python-提高-1

    阅读目录 1.GIL(全局解释器锁) 2.深拷贝.浅拷贝 3.私有化 4.import导入模块 5.再议 封装.继承.多态 一.GIL(全局解释器锁) GIL面试题如下 描述Python GIL的概念 ...

  8. dotnet6.0安装

    解压到目录 sudo mkdir -p /usr/share/dotnet && sudo tar zxf dotnet-sdk-6.0.423-linux-x64.tar.gz -C ...

  9. ubuntu16.04安装mmdetection库

    一,前言 1.1,更新 pip 和 conda下载源 1.2,查看 conda 和 pip 版本 二,MMDetection 简介 三,MMDetection 安装 3.1,依赖环境 3.2,安装过程 ...

  10. 用ffmpeg压缩视频、提高声音、加水印 命令记录

    ffmpeg -i in.mp4 -i LOGOW.png -filter_complex overlay=W-w:56 -c:v libx264 -x264-params "crf=35& ...