zoukankan      html  css  js  c++  java
  • go语言之并发编程 channel

    前面介绍了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] ",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]

  • 相关阅读:
    阿里P8架构师谈:阿里双11秒杀系统如何设计?
    秒杀系统设计的知识点
    秒杀系统架构优化思路
    秒杀系统解决方案
    Entity Framework Code First (七)空间数据类型 Spatial Data Types
    Entity Framework Code First (六)存储过程
    Entity Framework Code First (五)Fluent API
    Entity Framework Code First (四)Fluent API
    Entity Framework Code First (三)Data Annotations
    Entity Framework Code First (二)Custom Conventions
  • 原文地址:https://www.cnblogs.com/zhanghongfeng/p/10062381.html
Copyright © 2011-2022 走看看