前面介绍了goroutine的用法,如果有多个goroutine的话相互之间是如何传递数据和通信的呢。在C语言或者JAVA中,传输的方法包括共享内存,管道,信号。而在Go语言中,有了更方便的方法,就是channel。在同一时刻,仅有一个goroutine能向一个通道发送元素值,同时也仅有一个goroutine能从它那里接收元素值。在通道中,各个元素值都是严格按照发送到此的先后顺序排列的,最早被发送到通道的元素会最新被接收。因此通道相当于一个FIFO的队列。而且通道的元素也具有原子性,是不可被分割的。通道中的每一个元素值都只能被某一个goroutine接收,已被接收的的元素值会立刻从通道中删除。

channel的类型表示法:采用chan关键字

type intchan chan int

var intchan chan int

上面两种声明表示一个chan int类型的变量。初始化后,变量intchan就可以用来传递int类型的元素值了。那么如何从通道中发送数据以及接收数据呢。Channel中采用<- 以及->符号

<- intchan就表示从intchan中发送一个数据。intchan <- 就表示intchan接收一个数据

初始化通道

make(chan int,10)

这个表达式初始化了一个通道类型的值,传递给make函数的第一个参数表明,此值的具体类型是元素类型为int的通道类型。而第二个参数则指出该通道在同一时刻最多可以缓存10个元素值。当然,也可以在初始化一个通道的时候省略第二参数值,比如make(chan int)。如果第二个参数被忽略了,表示被初始化的这个通道永远无法缓存任何元素值,发送给它的元素值应该被立刻取走,否则发送发的goroutine就会被暂停,直到有接收方接收这个元素值。第二个参数大于0的可以称为缓冲通道,等于0的可以称为非缓冲通道。

strChan:=make(chan string,3)

如果要从这个通道中接收值,那么应该这样写代码 elem:=<-strChan。或者是elem,ok:=<-strChan。Ok是一个布尔变量值,赋值成功为true,失败为false

但是注意如果通道中没有任何元素时,当前的goroutine会被阻塞在此。如果在进行接收操作之前或过程当中该通道被关闭了。那么该操作会立即结束。并且变量elem会被赋予该通道的元素类型的零值。

来看一个使用channel的例子:代码如下

var strChan=make(chan string,3)

func main(){

synChan1:=make(chan struct{},1)

synChan2:=make(chan struct{},2)

go func(){

<-synChan1

fmt.Println("Received a sync signal and wait a second...[receiver]")

time.Sleep(time.Second)

for{

if elem,ok:=<-strChan;ok {

fmt.Println("Received:", elem, "[receiver]")

}else{

break

}

}

fmt.Println("Stopped.[receiver]")

synChan2 <- struct{}{}

}()

go func(){

for _,elem:=range[]string{"a","b","c","d"}{

strChan <- elem

fmt.Println("Sent:",elem,"[Sender]")

if elem == "c"{

synChan1 <- struct{}{}

fmt.Println("Sent a sync signal.[sender]")

}

}

fmt.Println("Wait for 2 seconds")

time.Sleep(time.Second*2)

close(strChan)

synChan2 <- struct{}{}

}()

<-synChan2

<-synChan2

}

运行结果如下:

Sent: a [Sender]

Sent: b [Sender]

Sent: c [Sender]

Sent a sync signal.[sender]

Received a sync signal and wait a second...[receiver]

Sent: d [Sender]

Wait for 2 seconds

Received: a [receiver]

Received: b [receiver]

Received: c [receiver]

Received: d [receiver]

Stopped.[receiver]

在这个例子中,先后启用了两个goroutine。分别用于演示在strChan之上的发送和接收操作。

发送操作的go函数:for循环中用于把切片的4个元素依次发送给strChan。当发送完第三个的时候,向synChan1发送了一个信号。这个信号会使接收方接收到后恢复执行。当for循环结束后,让当前的goroutine睡眠了2秒。这是为了等待接收方将4个值都接收完。再调用close关闭strChan通道。

接收操作的go函数:在synChan1收到信号前一直等待,当收到信号则恢复,说明strChan中已经有了3个元素。不过先睡眠1秒钟再去读。这是因为strChan的容量是3,所以发送方在这1秒内发送第4个值时会因strChan已满而等待。直到接收方从strChan取出一个值。发送方在关闭strChan后,接收方取不到正确的值,则退出循环

整个流程图可以参考下图

另外synChan2,这个channel的作用是为了不让主goroutine过早结束运行。只有在接收方和发送方都执行完了之后,synChan2才会有2个值在里面。

当接收方从通道接收到一个值类型的值时,对该值的修改就不会影响到发送发持有的那个值。但对于引用类型的值来说,这种修改会同时影响收发双方持有的值。看下面的例子

var mapChan=make(chan map[string]int,1)

func main(){

synChan:=make(chan struct{},2)

go func(){

for{

if elem,ok := <-mapChan;ok{

elem["count"]++

}else{

break

}

}

fmt.Println("stopped.[receiver]")

synChan <- struct{}{}

}()

go func(){

countMap:=make(map[string]int)

for i:=0;i<5;i++{

mapChan <- countMap

time.Sleep(time.Millisecond)

fmt.Println("The count map:%v.[sender]\n",countMap)

}

close(mapChan)

synChan <- struct{}{}

}()

<-synChan

<-synChan

}

运行结果:

The count map:.[sender] map[count:1]

The count map:.[sender] map[count:2]

The count map:.[sender] map[count:3]

The count map:.[sender] map[count:4]

The count map:.[sender] map[count:5]

stopped.[receiver]

go语言之并发编程 channel的更多相关文章

  1. go语言之并发编程 channel(1)

    单向channel: 单向通道可分为发送通道和接收通道.但是无论哪一种单向通道,都不应该出现在变量的声明中,假如初始化了这样一个变量 var uselessChan chan <- int =m ...

  2. Go语言 7 并发编程

    文章由作者马志国在博客园的原创,若转载请于明显处标记出处:http://www.cnblogs.com/mazg/ Go学习群:415660935 今天我们学习Go语言编程的第七章,并发编程.语言级别 ...

  3. 十.Go并发编程--channel使用

    一.设计原理 Go 语言中最常见的.也是经常被人提及的设计模式就是: "不要通过共享内存来通信,我们应该使用通信来共享内存" 通过共享内存来通信是直接读取内存的数据,而通过通信来共 ...

  4. Go语言之并发编程(二)

    通道(channel) 单纯地将函数并发执行是没有意义的.函数与函数间需要交换数据才能体现并发执行函数的意义.虽然可以使用共享内存进行数据交换,但是共享内存在不同的goroutine中容易发生竞态问题 ...

  5. Go语言之并发编程(一)

    轻量级线程(goroutine) 在编写socket网络程序时,需要提前准备一个线程池为每一个socket的收发包分配一个线程.开发人员需要在线程数量和CPU数量间建立一个对应关系,以保证每个任务能及 ...

  6. Go语言之并发编程(四)

    同步 Go 程序可以使用通道进行多个 goroutine 间的数据交换,但这仅仅是数据同步中的一种方法.通道内部的实现依然使用了各种锁,因此优雅代码的代价是性能.在某些轻量级的场合,原子访问(atom ...

  7. Go语言之并发编程(三)

    Telnet回音服务器 Telnet协议是TCP/IP协议族中的一种.它允许用户(Telnet客户端)通过一个协商过程与一个远程设备进行通信.本例将使用一部分Telnet协议与服务器进行通信. 服务器 ...

  8. go语言之并发编程同步一

    前面介绍了采用go语法的并行操作以及channel.既然是并行操作,那么就涉及到数据原子性以及同步的问题.所以在Go里面也需要采用同步的机制. 互斥锁: 由标准库代码包sync中的Mutex结构体类型 ...

  9. go语言入门(10)并发编程

    1,概述 1.1并发和并行 并行(parallel):指在同一时刻,有多条指令在多个处理器上同时执行. 并发(concurrency):指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行, ...

随机推荐

  1. Singleton单例模式是最简单的设计模式,它的主要作用是保证在程序执行生命周期中,使用了单类模式的类仅仅能有一个实例对象存在。

                                                                                                        ...

  2. java中不能用小数点(.)来做分隔符

    split()括号里是一个String的参数,所以一定要符合这种:split(".")形式,即点'.'要用双引号""括起来"."在java中 ...

  3. 如何去掉MapReduce输出的默认分隔符

    我们在用MapReduce做数据处理的时候,经常会遇到将只需要输出键或者值的情况,如context.write(new Text(record), new Text("")),这样 ...

  4. surface4 笔盖失灵的解决方案

    http://tieba.baidu.com/p/3670357234 先找到设备管理器,找到蓝牙,删除里面所有的设备.然后重启. 之后再次找到蓝牙,匹配pen.就可以用了. 解决的前提是:我确定笔帽 ...

  5. GetTickCount()函数的陷阱!

    开发中经经常使用GetTickCount()函数来进行间隔时间的推断.如推断某一段代码运行花了多少时间等,使用比較方便. 可是仅仅针对寻常的一些測试.近期开发一个服务程序时,也在代码中用GetTick ...

  6. 我佩服-WPF(2)

    简单的学学WPF,我们知道他就是拖拉控件,一点也不难.假设公司真的使用WPF搞开发,你去面试,直接说WPF就是拖拉控件,那就慘了. 有些公司非常喜欢使用WPF.不不过比較简单.更重要的是全然做到了分离 ...

  7. Android应用架构之MVP---&gt;天气实例

    我们知道.Android App 本质上抽象成两个层次:视图和数据.为了App在发展过程中高速的适应变化,方便维护和高速迭代,我们要将数据和视图解耦,而在解藕方面我们的前辈们在漫长的软件开发经验中为我 ...

  8. win10--vs2015--libjpeg--64位库的编译过程记录

    win10--vs2015--libjpeg--64位库的编译过程记录 1. 下载源代码:   http://libjpeg.sourceforge.net/    或者  http://www.ij ...

  9. html中keydown事件

    实现在输入框按回车按钮进行查询的功能: 1.<input type="text" id="inputChannel" onkeydown="ke ...

  10. NHibernate 数据查询之Linto to NHibernate (第八篇)

    NHibernate 数据查询之Linto to NHibernate (第八篇) 刚学NHibernate的时候觉得,HQL挺好用的,但是终归没有与其他技术 相关联,只有NHibernate用到,一 ...