[管道]

分为 有缓冲和无缓冲两种

无缓冲的与有缓冲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. .NET 最好用的验证组件 FluentValidation

    前言 一个 .NET 验证框架,支持链式操作,易于理解,功能完善,组件内提供十几种常用验证器,可扩展性好,支持自定义验证器,支持本地化多语言. 项目介绍 FluentValidation 是一个开源的 ...

  2. C++ STL vector 性能之push_back、emplace_back、reserve

    #include <iostream> #include <vector> #include <chrono> using namespace std; const ...

  3. Rounding

    前言 以前写过一篇关于 Rouding 的 decimal, double, float, 但有点杂乱, 这篇做一个整理. Why need rouding? 除法会诞生小数. 甚至会诞生无限小数 ( ...

  4. Git冲突解决技巧

    在多人协作的软件开发项目中,Git 冲突是不可避免的现象.当两个或更多的开发者同时修改了同一段代码,并且尝试将这些修改合并到一起时,冲突就发生了.解决这些冲突是确保代码库健康和项目顺利进行的关键.以下 ...

  5. Log4j2—漏洞分析(CVE-2021-44228)

    目录 Log4j2漏洞原理 漏洞根因 调用链源码分析 调用链总结 漏洞复现 dns rmi Log4j2漏洞原理 前排提醒:本篇文章基于我另外一篇总结的JNDI注入后写的,建议先看该文章进行简单了解J ...

  6. 加快 hdfs block 块复制的参数调整

    共涉及三个参数: dfs.namenode.replication.max-streams 30 => 70 dfs.namenode.replication.max-streams-hard- ...

  7. 控制请求并发数量:p-limit 源码解读

    p-limit 是一个控制请求并发数量的库,他的整体代码不多,思路挺好的,很有学习价值: 举例 当我们同时发起多个请求时,一般是这样做的 Promise.all([ requestFn1, reque ...

  8. duxapp:基于Taro使用模块化开发,提升开发效率

    duxapp是基于Taro二次开发的模块化框架 使用这个框架,结合框架提供的UI库和工具库,能帮助你快速且高质量的完成项目,且能实现同时开发小程序.H5.APP(React Native),并且保证各 ...

  9. 2款.NET开源且免费的Git可视化管理工具

    Git是什么? Git是一种分布式版本控制系统,它可以记录文件的修改历史和版本变化,并可以支持多人协同开发.Git最初是由Linux开发者Linus Torvalds创建的,它具有高效.灵活.稳定等优 ...

  10. 八字五行强弱喜用神测算api免费接口_json数据格式_可计算五行打分强弱

    八字以木.火.土.金.水去分析其旺弱,而分析之法以月令地支最为有力,其次为时支,再次为日支,最弱为年支,而天干必须见地支有同类或有生自己的五行才有用,因为只有天干一个单独的五行,其力会弱至无用.这个接 ...