博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Golang通道Channel详解
阅读量:5756 次
发布时间:2019-06-18

本文共 1999 字,大约阅读时间需要 6 分钟。

通道类型是Go自带的,相当于是一个先进先出的队列,同时唯一一个可以满足并发安全性的类型。声明一个通道类型变量的时候,首先需要确定通道类型的元素类型,然后还要确定通道的容量,当然默认容量是0。

初始化

使用make进行初始化,如下所示:

c := make(chan int)c := make(chan string, 10)复制代码

如果不指定容量,默认通道的容量是0,这种通道也成为非缓冲通道。

通道的发送和接收特性

  1. 对于同一个通道,发送操作之间是互斥的,接收操作之间也是互斥的。

    简单来说就是在同一时刻,Go的运行系统只会执行对同一个通道的任意个发送操作中的某一个,直到这个元素值被完全复制进该通道之后,其他发送操作才会执行。针对接收操作也是这样。 对于通道中的同一个值,发送操作和接收操作也是互斥的。如正在被复制进通道但还未复制完成的元素值,这时接收方也不会看到和取走。

    tips 元素值从外界进入通道会被复制。也就是说进入通道的并不是在接收操作符右边的那个元素值,而是他的副本。

  2. 发送操作和接收操作中对元素值的处理都是不可分割的。 不可分割意思就是发送操作要么还没复制元素,要么已经复制完毕,不会出现值只复制了一部分的情况。

  3. 发送操作在完全完成之前会被阻塞。接收操作也是如此。 发送操作包括,“复制元素值”,“放置副本到通道内” 二个步骤。在这二个步骤完成之前,发送操作会一直阻塞,他之后的代码是不会执行的。 接收操作包括“复制通道内元素值”,“放置副本到接收方”,“删除原值” 三个操作。这三个操作在完成之前也是会一直阻塞的。

tips: 上面讲的复制都属于浅拷贝。浅拷贝只是拷贝值以及值中直接包含的东西,深拷贝就是把所有深层次的结构一并拷贝,Golang只有浅拷贝。

发送操作和接收操作在什么时候会被阻塞呢

对于缓存通道

  • 如果通道已满,所有的发送操作就会阻塞,直到通道中有元素被取走
  • 如果通道已空,所有的接收操作就会阻塞,直到通道中有新的元素

对于非缓存通道

  • 无论发送操作还是接受操作一开始就是阻塞的,只有配对的操作出现才会开始执行。

收发操作何时会引起panic

  • 通道关闭,在进行发送操作会引发panic

  • 关闭一个已经关闭的通道也会引发panic 更具体地说,当我们把接收表达式的结果同时赋给两个变量时,第二个变量的类型就是一定bool类型。它的值如果为false就说明通道已经关闭,并且再没有元素值可取了。

    注意,如果通道关闭时,里面还有元素值未被取出,那么接收表达式的第一个结果,仍会是通道中的某一个元素值,而第二个结果值一定会是true。因此,通过接收表达式的第二个结果值,来判断通道是否关闭是可能有延时的。

    package mainimport "fmt"func main() {	ch1 := make(chan int, 2)	// 发送方。	go func() {		for i := 0; i < 10; i++ {			fmt.Printf("Sender: sending element %v...\n", i)			ch1 <- i		}		fmt.Println("Sender: close the channel...")		close(ch1)	}()	// 接收方。	for {		elem, ok := <-ch1		if !ok {			fmt.Println("Receiver: closed channel")			break		}		fmt.Printf("Receiver: received an element: %v\n", elem)	}	fmt.Println("End.")}复制代码

Channel引起的死锁的常见场景

死锁是指两个或两个以上的协程的执行过程中,由于竞争资源或由于彼此通信而造成的一种阻塞的现象,若无外力作用,他们将无法推进下去,解决死锁的方法是加锁。。结合上面讲的channel相关知识,大家可以思考一下面情况为何为引起死锁。

场景1:一个通道在一个go协程读写

func main() {	c:=make(chan int)	c<-666	<-c}复制代码

场景二:go程开启之前使用通道

func main() {	c:=make(chan int)	c<-666	go func() {		<-c	}()}复制代码

场景三:通道1中调用了通道2,通道2中调用通道1

func main() {	c1,c2:=make(chan int),make(chan int)	go func() {		for  {			select{				case <-c1:					c2<-10			}		}	}()	for  {		select{		case <-c2:			c1<-10		}	}	}复制代码

转载地址:http://dunkx.baihongyu.com/

你可能感兴趣的文章
.Net组件程序设计之远程调用(二)
查看>>
ant中文教程
查看>>
Linux常用命令(一)
查看>>
WSUS数据库远端存储条件下切换域及数据库迁移
查看>>
【VMCloud云平台】SCAP(四)租户(一)
查看>>
linux释放内存的方法
查看>>
基于 Android NDK 的学习之旅----- C调用Java
查看>>
我的友情链接
查看>>
Android图形显示系统——下层显示4:图层合成上(合成原理与3D合成)
查看>>
Windows 10 技术预览
查看>>
Tomcat http跳转https
查看>>
一个自动布署.net网站的bat批处理实例
查看>>
tomcat 安装
查看>>
AIX:物理卷及有关概念
查看>>
我的友情链接
查看>>
Centos6.6安装选包及基础场景说明
查看>>
java基础面试题-1
查看>>
深克隆与序列化效率的比较
查看>>
lamp+nginx代理+discuz+wordpress+phpmyadmin搭建一
查看>>
nagios监控使用139邮箱报警
查看>>