• 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]

  • 相关阅读:
    万字长文:大规模 Elasticsearch 高可用集群环境调优实践
    jenkins 配置。
    Xcode的多种Build Configuration
    FZUOJ 2214 Knapsack problem 背包
    Atcoder 070 D Transit Tree Path
    POJ 3903 Stock Exchange LIS
    POJ 2533 Longest Ordered Subsequence 简单DP
    HDU 1260 Tickets 简单DP
    HDU 1114 Piggy-Bank 简单DP
    HDU 1176 免费馅饼 简单DP
  • 原文地址:https://www.cnblogs.com/zhanghongfeng/p/10062381.html
Copyright © 2020-2023  润新知