go语言修炼之九-并发channel通信

go语言修炼之九

channel

在之前第六、七章,介绍了go语言的协程并发机制,以及主程与协程采用sync包进行阻塞等待的工作方案。这次看看go语言官方推荐的协程并发解决方案–channel。

channel解决两个问题:
一是协程间的数据交互(在六、七节里面没有涉及)
二是协程间的阻塞方式

channel说明

创建一个channel用关键字make,指定类型为chan,关闭一个channel用close
保持良好习惯,使用完的channel就关闭,在数据输入侧(生产侧)进行关闭操作。

ch1 := make(chan int)
ch2 := make(chan int,10)

如上,我们创建一个无缓冲的管道ch1,以及一个允许缓冲10个int 数据的管道ch2。
固定用法:channel要求指明传输的数据类型,比如int,string等。
管道里传输和读取数据使用箭头 <- 符号,数据流入和流出看箭头指向。

//向管道传数据
var a:=3
ch1<-a

//从管道取数据
var b int
b = <-ch1

继续下面的代码:

package main

import (
	"fmt"
	"time"
)

func worker1(v chan int) {
	defer close(v)

	for i := 0; i < 5; i++ {
		fmt.Println("input i=", i)
		v <- i
	}
}

func main() {


	ch1 := make(chan int)
	go worker1(ch1)

	for {
		time.Sleep(time.Second * 1)
		a, b := <-ch1
		fmt.Println("output a=", a)
		fmt.Println("output b=", b)
	}

}

我们先定义一个worker1,他接收一个channel,负责往这个channel里写数据,因为worker1算是生产者(产生数据端),所以他负责关闭channel,这里使用的是defer(close)方法。
在主程中,我们开启一个协程跑worker1,主程则循环从channel里取数据。主程与协程通过管道ch1来交流数据。执行结果如下。
在这里插入图片描述
这里a,b := <- ch1,是从ch1里取值,a代表真实的数据,b代表channel的状态。
当channel状态正常时,b 为 true,当channel被关闭时,b为false,但a此时读取的是管道指定传输类型的零值。

所以可以使用b进行逻辑判断,是否退出。另一种更为常用的写法,是使用range。

package main

import (
	"fmt"
)

func worker1(v chan int) {
	defer close(v)

	for i := 0; i < 5; i++ {
		fmt.Println("input i=", i)
		v <- i
	}
}

func main() {

	ch1 := make(chan int)
	go worker1(ch1)

	for a := range ch1 {
		fmt.Println("output a=", a)
	}
}

在这里插入图片描述
如上,range 后面直接写管道名,会默认读取管道数据,检测到管道关闭,range自身就会退出。

我们再看看channel的阻塞特点,如上代码,让worker1一直循环不再退出,主程将一直处于等待态

package main

import (
	"fmt"
)

func worker1(v chan int) {
	defer close(v)

	for i := 0; i < 5; i++ {
		fmt.Println("input i=", i)
		v <- i
	}
	for {

	}
}

func main() {

	ch1 := make(chan int)
	go worker1(ch1)

	for a := range ch1 {

		fmt.Println("output a=", a)

	}

}

因为worker1后面有空循环,所以永远执行不到close(ch1),这时虽然管道ch1内没有任何数据,但管道本身有效,并未关闭,所以range不会退出,故主程会以阻塞态等待ch1后续有数据产生或者直到ch1关闭为止。
在这里插入图片描述

其他

有些地方把go里采用有缓冲的管道称为非阻塞模式或者异步模式,采用了无缓冲的管道成为阻塞或者同步模式,其实缓冲只是给你的程序设计有个缓冲数据的空间,本质上仍然是阻塞的机制。只不过有缓冲区的时候,一开始并不会触发阻塞条件。如果及时处理缓冲内的数据,或者缓冲区给的足够大,程序跑起来,就像是一个非阻塞或者异步的表象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本书作者带你一步一步深入这些方法。你将理解 Go语言为何选定这些并发模型,这些模型又会带来什么问题,以及你如何组合利用这些模型中的原语去解决问题。学习那些让你在独立且自信的编写与实现任何规模并发系统时所需要用到的技巧和工具。 理解Go语言如何解决并发难以编写正确这一根本问题。 学习并发与并行的关键性区别。 深入到Go语言的内存同步原语。 利用这些模式中的原语编写可维护的并发代码。 将模式组合成为一系列的实践,使你能够编写大规模的分布式系统。 学习 goroutine 背后的复杂性,以及Go语言的运行时如何将所有东西连接在一起。 作者简介 · · · · · · Katherine Cox-Buday是一名计算机科学家,目前工作于 Simple online banking。她的业余爱好包括软件工程、创作、Go 语言(igo、baduk、weiquei) 以及音乐,这些都是她长期的追求,并且有着不同层面的贡献。 目录 · · · · · · 前言 1 第1章 并发概述 9 摩尔定律,Web Scale和我们所陷入的混乱 10 为什么并发很难? 12 竞争条件 13 原子性 15 内存访问同步 17 死锁、活锁和饥饿 20 确定并发安全 28 面对复杂性的简单性 31 第2章 对你的代码建模:通信顺序进程 33 并发与并行的区别 33 什么是CSP 37 如何帮助你 40 Go语言并发哲学 43 第3章 Go语言并发组件 47 goroutine 47 sync包 58 WaitGroup 58 互斥锁和读写锁 60 cond 64 once 69 池 71 channel 76 select 语句 92 GOMAXPROCS控制 97 小结 98 第4章 Go语言并发模式 99 约束 99 for-select循环103 防止goroutine泄漏 104 or-channel 109 错误处理112 pipeline 116 构建pipeline的最佳实践 120 一些便利的生成器 126 扇入,扇出 132 or-done-channel 137 tee-channel 139 桥接channel模式 140 队列排队143 context包 151 小结 168 第5章 大规模并发 169 异常传递169 超时和取消 178 心跳 184 复制请求197 速率限制199 治愈异常的goroutine 215 小结 222 第6章 goroutine和Go语言运行时 223 工作窃取223 窃取任务还是续体 231 向开发人员展示所有这些信息 240 尾声 240 附录A 241
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值