• 如何安全close go 的channel


    golang 在多协程下,  不清楚谁是 sender 谁是 recver 的时候, close(chan) 和 chan<-data 两个都很容易报错

     原则:

    //1  close 永远和data分离, 不要close 还会发送或接受的 channel
    //2  close 永远要做好 'close of closed channel' 错误的发生
    //3  send (也就是 <- 操作) 永远不要让他出现 'send to a nil channel' 其实也就是第一点

    那么close 到底怎么才能更安全,更正确的,解决呢? 

    单协程 直接close ,太low,我们不考虑,只考虑最复杂环境下的情况.

     第一种方法 暴力关闭channel ,只需要增加 recover() 就可以了

    第二种方法  通过对变量加锁,或者直接使用sync.Once 来控制 close 只有一次

     第三种方法 创建两个chan,一个永远不close,只进行 无限 发送,  单个接收; 一旦接收了 ,就关闭另外一个; 另外一个 返还给外部, 只判断 close的情况,不进行发送和直接close

    第四种方法 比较简单,总是 使用select就可以了,

    done := make(chan struct{})

    // 复杂环境下 并发 进行关闭
    select{
          case <-done:
          default:
                    close(done)
    }

     

    //复杂环境 并发 检测是否关闭
    
    
    select{
          case <-done
                 //关闭处理
                 return 
           default:
                //没有关闭的处理
    }
    
    

     经过高并发测试,发现 上面的select 关闭方法, 还是不安全, select不是原子性, 只要异步 数量达到值,就还是会有出现panic的情况;

    测试用例:

     运行  Test_SafeCloseChan2   几乎是必现的,只不过是  成功 END 出现几次罢了.

    func Test_SafeCloseChan2(t *testing.T) {
        for i := 0; i < 1000; i++ {
            Test_SafeCloseChan(t)
        }
    }
    func Test_SafeCloseChan(t *testing.T) {
        done := make(chan bool)
        begin := time.Now().Add(300 * time.Millisecond)
        wg := sync.WaitGroup{}
        for i := 0; i < 1000000; i++ {
            wg.Add(1)
            go func(i int) {
                defer func() {
                    wg.Done()
                }()
                //等待开始,一起开始,避免不同时
                for {
                    if time.Now().After(begin) {
                        break
                    }
                    time.Sleep(1 * time.Microsecond)
                }
    
                select {
                case <-done:
                    //case <-time.After(500 * time.Millisecond):
                    //    select {
                    //    case <-done:
                    //    default:
                    //        close(done)
                    //        t.Log("Success close(done)")
                    //    }
                    //case <-time.After(500 * time.Millisecond):
                    //    t.Log("Success close(done)")
                    //    close(done)
                default:
                    close(done)
                    t.Log("Success close(done)")
                }
            }(i)
        }
        wg.Wait()
        t.Log("=============END=========")
    }

    所以, 还是加 panic 吧

    https://zhuanlan.zhihu.com/p/32529039

  • 相关阅读:
    ProxySQL!像C罗一样的强大!
    zookeepr 临时节点
    JavaScript 的闭包用于什么场景
    JavaScript 的闭包用于什么场景
    JavaScript 的闭包用于什么场景
    JavaScript 的闭包用于什么场景
    随笔
    随笔
    随笔
    随笔
  • 原文地址:https://www.cnblogs.com/ayanmw/p/14150576.html
Copyright © 2020-2023  润新知