20210225关于“管道泄漏”问题,我是不是想的太复杂了 - ziyouzy/2021blog GitHub Wiki

会不会根本的原则是,只要使用方式正确,就不会出现问题 或者说,使用管道时只要基于正确的“数据流动设计模式”,就不会出问题 是否可以这样认为,数据流动模式无非是由这三种元素构成:

  1. 创建管道
  2. 联通管道
  3. 向管道注入数据 这是三件不同的事

创建管道很简单:

ch :=make(chan []byte)

联通管道则是借助for range结构:

for b := range ch{
    newch <- b
}

但是纯粹的for range{}结构会直接在b := range ch这一行就发生阻塞(读管道时管道为空会立刻堵塞)
同时当进行第二句newch <- b时如果newch存在旧数据也会发生阻塞(写管道时管道不为空会立刻阻塞)
或者说,造成管道“泄漏”的原因无非是这两种:读管道时管道为空会立刻堵塞与写管道时管道不为空会立刻阻塞
同时,for range{}结构其实等同于"b <- ch",也就是说,一旦出现了ch <- X与X := <- ch、<- ch这样的情况就要小心了,要小心“<-”操作

于是如上的for range{}加newch <- x结构存在两个可能发生“泄漏的点”
反过来讲,这恰恰也是“连接管道”逻辑结构的特征,为了让程序不阻塞,需要用一个携程对其包裹:

go func(){
    for b := range oldch{
        newch <- b
    }
}()

同时,我们目前探讨的场景均是还未开始“向管道注入数据”,因此虽然携程可以确保主线程不卡顿,但是对于这个子携程来说,已经在“for b := range oldch”这句被阻塞住了,因为oldch无数据流动

因此也就是说,这样的结构即使最典型的管道连接器:

go func(){
    for b := range oldch{
        newch <- b
    }
}()

也就是目前所尝试设计的各个func XXXConnectYYY()函数,换句话说,将“连接管道”这一操作高度的从整体逻辑中抽象出来,他本身并不是导致“管道泄漏”的根本原因(创建管道更不是)

最后的最后就是“向管道注入数据”了,这个环节体现在代码上,会是以某种形式的“数据源”出现,如将某个ip的net.Conn封装成一个river-node,而该river-node定会拥有自身的News
拿到News的同时,也就完成了一次“向管道注入数据”,也就是说,“向管道注入数据”定会通过river-node来实现
而他依然不是造成管道泄漏的根本原因。

而真正的原因是,某个连接器(Connector)的析构不当,或者某个river-node的析构不当,以及从这两个问题所派生出的,包含这两者作为内部字段的复杂数据类型的析构不当,才会导致管道泄漏