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


    单向channel:

    单向通道可分为发送通道和接收通道。但是无论哪一种单向通道,都不应该出现在变量的声明中,假如初始化了这样一个变量

    var uselessChan chan <- int =make(chan <- int,10)

    这样一个变量该如何使用呢,这样一个只进不出的通道没有什么实际意义。那么这种单向通道的应用场景在什么地方呢。我们可以用这种变换来约束对通道的使用方式。比如下面的这种声明

    func Notify(c chan <- os.Signal, sig… os.Signal)

    该函数的第一个参数的类型是发送通道类型。从参数的声明来看,调用它的程序应该传入一个只能发送而不能接收的通道。但是实际上应该传入的双向通道,Go会依据该参数的声明,自动把它转换成一个单向通道。Notify函数中的代码只能向通道c发送数据,而不能从它那里接收数据,在该函数中从通道c接收数据会导致编译错误。但是在函数之外不存在这个约束。

     

    现在对SignalNotifier接口的声明稍作改变,如下:

    type SignalNotifier interface{

        Notify(sig…os.Signal) <- chan os.Signal

    }

    现在这个声明放在了函数外面,这种实现方法说明Notify方法的调用只能从作为结果的通道中接收元素值,而不能向其发送元素值。来看一个单向通道的例子

     

    var strChan=make(chan string,3)

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

     

    func receive(strChan <- chan string,syncChan1 <- chan struct{},syncChan2 chan <- struct{}){

            <-syncChan1

            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]")

            syncChan2 <- struct{}{}

    }

     

    func send(strChan chan <- string,syncChan1 chan <- struct{},syncChan2 chan <- struct{}){

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

                     strChan <- elem

                     fmt.Println("sent:",elem,"[sender]")

                     if elem == "c"{

                             syncChan1 <- struct{}{}

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

                     }

            }

            fmt.Println("wait 2 seconds...[sender]")

            time.Sleep(time.Second*2)

            close(strChan)

            syncChan2 <- struct{}{}

    }

     

    func main(){

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

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

            go receive(strChan,syncChan1,syncChan2)

            go send(strChan,syncChan1,syncChan2)

            <-syncChan2

            <-syncChan2

    }

    receive函数只能对strChan和syncChan1通道进行接收操作。而send函数只能对这2个通道进行发送操作。区别点在于chan 和 <-的位置。chan <- 表明是接收通道。<- chan表明是发送通道。运行结果如下:

    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]

    Received: a [receiver]

    Received: b [receiver]

    Received: c [receiver]

    Received: d [receiver]

    Received: e [receiver]

    sent: e [sender]

    wait 2 seconds...[sender]

    stopped.[receiver]

     

    非缓冲channel

    如果在初始化一个通道时将其容量设置为0或者直接忽略对容量的设置。就会使该通道变成一个非缓冲通道。和异步的方式不同,非缓冲通道只能同步的传递元素值

    1 向此类通道发送元素值的操作会被阻塞。直到至少有一个针对通道的接收操作进行为止。该接收操作会首先得到元素的副本,然后再唤醒发送方所在的goroutine之后返回。也就是说,这是的接收操作会在对应的发送操作完成之前完成。

    2 从此类通道接收元素值的操作会被阻塞,直到至少有一个针对该通道的发送操作进行为止。发送操作会直接把元素值赋值给接收方,然后再唤醒接收方所在的goroutine之后返回。这时的发送操作会在对应的接收操作之前完成。

    func main(){

            sendingInterval:=time.Second

            receptionInterval:=time.Second*2

            intChan:=make(chan int,0)

            go func(){

                     var ts0,ts1 int64

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

                             intChan <- i

                             ts1=time.Now().Unix()

                             if ts0 == 0{

                                      fmt.Println("sent:",i)

                             }else{

                                      fmt.Println("Sent:",i,"[interval:",ts1-ts0,"] ")

                             }

                             ts0=time.Now().Unix()

                             time.Sleep(sendingInterval)

                     }

                     close(intChan)

            }()

            var ts0,ts1 int64

            Loop:

            for{

                     select{

                     case v,ok:=<-intChan:

                             if !ok{

                                      break Loop

                             }

                         ts1=time.Now().Unix()

                             if ts0 == 0{

                                      fmt.Println("receive:",v)

                             }else{

                                      fmt.Println("receive:",v,"[interval:",ts1-ts0,"] ")

                             }

                     }

                     ts0=time.Now().Unix()

                     time.Sleep(receptionInterval)

            }

            fmt.Println("End.")

    }

    运行结果:

    sent: 1

    receive: 1

    receive: 2 [interval: 2 ]

     

    Sent: 2 [interval: 2 ]

     

    Sent: 3 [interval: 2 ]

     

    receive: 3 [interval: 2 ]

     

    receive: 4 [interval: 2 ]

     

    Sent: 4 [interval: 2 ]

     

    receive: 5 [interval: 2 ]

     

    Sent: 5 [interval: 2 ]

     

    End.

    可以看到发送操作和接收操作都与receptioninterval的间隔一致。如果把sendingInterval改成time.Second*4. 则结果如下:发送操作和接收操作都与sendingInterval的间隔一致

    sent: 1

    receive: 1

    Sent: 2 [interval: 4 ]

     

    receive: 2 [interval: 4 ]

     

    Sent: 3 [interval: 4 ]

     

    receive: 3 [interval: 4 ]

     

    Sent: 4 [interval: 4 ]

     

    receive: 4 [interval: 4 ]

     

    Sent: 5 [interval: 4 ]

     

    receive: 5 [interval: 4 ]

     

    End.

  • 相关阅读:
    Oracle创建用户并赋予权限
    Oracle查询表空间使用情况
    Oracle更改数据库文件大小、实时增加文件容量
    Oracle查询数据中占用空间最大的表
    Oracle存储包存储及案例
    Oracle包Package调用Package
    Oracle存储过程Procedure语法及案例
    Oracle存储过程function语法及案例
    Oracle常用语法
    Oracle游标循环更新数据案例
  • 原文地址:https://www.cnblogs.com/zhanghongfeng/p/10075786.html
Copyright © 2020-2023  润新知