5

最近在开发过程当中遇到了几个goroutine通信的问题,我觉得这几个问题非常具有代表性,因此拿出来和大家分享一下。

检查channel是否关闭

开发过程当中有遇到这样的一种情况,需要检查channel是否关闭,如果关闭则不进行相应操作,否则会panic等现象。在golang的select语法当中,default分支可以解决上述问题,请看如下例子:

closechan := make(chan int,0)
dchan := make(chan int,0)

select{
    case <- closechan:
        fmt.Println("channel already closed.")
        return
    default:
        fmt.Println("channel not closed, do your things")  
        dchan <- 1 //1
}

  go func(){
     for{
        select{
          case data := <- dchan: //2
             err := doSomeThing(data) //3
             if err != nil /* some thing wrong*/ { //4
               close(closechan) //5
            }   
       }
    }
  }

上述的方式可以在处理dchan的数据的时候处理异常并不再接受来自dchan的数据。

但是我们需要考虑一种情况,如果在doSomeThing(data)的过程当中,出现异常(4),那么不允许往dchan发送数据,并将closechan关闭。在(5)关闭closechan之后就不会再进入(1)的default流程了。

可是这还有问题,那就是如果doSomeThing(data)处理的过程当中,新来了一个数据进入到了dchan,那么将会在(1)(2)处阻塞,当doSomeThing(data)处理完之后,还会多处理一次异常情况,也就是说在(5)处将会close(closechan)两次,这样会导致panic: close of closed channel,所以我们需要在(5)再写一个相应的default处理逻辑:

go func(){
    for{
        select{
            case data := <- dchan: //2
            err := doSomeThing(data) //3
                if err != nil /* some thing wrong*/ { //4
                    select{
                        case <-closechan:
                        //do nothing
                          return
                        default:
                          close(closechan) //5
                    }
                }   
        }
    }
}

检查buffered-channel是否已满

当我们在使用bufferd-channel的时候,我们可能需要检查当前的channel是否已经满了,因为我们可能不希望此时goroutine阻塞,所以可以采用如下的方式进行处理:

cc := make(chan int, 1)

cc <- data1

select {
    case cc <- data2:
        fmt.Println("data already en-channel")
    default:
        fmt.Println("cc was full")
}

fan-in

在研究并发map的时候,会考虑到一种shard-map的实现方式,在读取map中的值的时候,需要通过多个小map中获取完整的map值,可以利用channel实现fan-in:

func fanIn(chans []chan int, out chan int) {
    wg := sync.WaitGroup{}
    wg.Add(len(chans))
    for _, ch := range chans {
        go func(ch chan int) {
            for t := range ch {
                out <- t
            }
            wg.Done()
        }(ch)
    }
    wg.Wait()
    close(out)
}

terasum
453 声望51 粉丝

Blockchain从业者,Go, JavaScript, Haskell爱好者,函数式编程,高性能并发。