Skip to content

链接关闭的时候有概率发生 panic #400

Description

@NeverENG

在跟着《深入理解GO语言》搓框架,在测试的时候发现在关闭的时候有概率发生panic,原因是 sent to closed channle
极小概率,同时看到 issue 中有人提了一下但是不被重视,个人建议要不要加一个recover避免出问题

Activity

  1. aceld commented on Apr 28, 2026

    @aceld
    Owner

    @NeverENG 你看下最新的master代码会不会出现你说的阻塞情况,出现可以将堆栈信息贴一下,Thanks

  2. aceld commented on May 4, 2026

    @aceld
    Owner

    @NeverENG

    这个 send on closed channel / send to closed channel 的 panic 确实一般是“关闭连接时并发 send”导致的竞态问题,光靠 recover 只能兜底,根因还是要把关闭/发送的时序和状态收敛好。

    我这边看了下 master 目前的实现,确实存在一个典型风险点:

    • 在 TCP 连接的 SendToQueue 里,如果触发了 case <-c.ctx.Done(): 分支,会直接 close(c.msgBuffChan)(见 znet/connection.go),但与此同时其他 goroutine 可能还在往 c.msgBuffChan <- data 发送,从而小概率触发 send on closed channel。
    • WebSocket/KCP 的实现里关闭 msgBuffChan 的路径不完全一样(WS/KCP 主要在 finalizer() 里 close),但同类竞态也可能发生在“关闭与发送并发”的场景中。

    所以建议你这边先补充两点信息,方便我们定位到具体触发路径:

    1. 你用的是 TCP / WebSocket / KCP 哪种模式?
    2. 能否贴一下 panic 的 完整堆栈(尤其是指向哪个文件/哪一行触发了 send on closed channel)?

    短期兜底当然可以在 goroutine 入口加 recover,避免进程被打挂;但更推荐的修复方向是:

    • 关闭通道要做到 只关闭一次、并且发送方不直接 close(或用 sync.Once 控制 close);
    • 发送时用“连接状态 + ctx.Done()”提前返回,避免对已关闭通道发送;
    • 或者统一由 writer goroutine 负责关闭通道,其他发送方只写、不关。

    你把堆栈贴出来后,我可以进一步确认是 TCP 的 SendToQueue 这条路径,还是 WS/KCP 的关闭路径,并给出更准确的修复建议/PR 方向。

  3. NeverENG commented on May 4, 2026

    @NeverENG
    ContributorAuthor
  4. NeverENG commented on May 5, 2026

    @NeverENG
    ContributorAuthor

    @aceld 我在下游fork了仓库,构建了稳定浮现 panic 的代码,目前的maste的代码不会出现 send to close chanel的问题,但是测出来了另一个问题,close the close chanel,我这边想要加入 sync.Once 修复这个 bug ,您意下如何
    Image

  5. redfox1999 commented on May 11, 2026

    @redfox1999
    Contributor

    我正想来说这个问题,虽然我没遇到这个问题,但在看代码时,我发现会有这个风险。主要是Close是直接关闭网络连接,而这时。可能还有pendding的 读写操作。最好是能做到时序一致性。就是在 Read 协程里。只能把状态改为关闭中。如果再收到新的数据,发现是在 CloseWaiting 就丢弃不管了。而且如果是上层需要 Send时,发现是 CloseWaiting 也丢弃或返回error 。让Write协程把所有需要发的数据发完以后,再Close.。
    另外还有个问题,本来是想单独说的,但看到作者都在这里回了,就也在这里说了吧。 connection有两个 Send, 一个是 SendMsg, 一个是SendBuffMsg 。开始我没明白有什么区别,后来看了代码,才发现SendMsg是直接调用了网络层直接Send, SendBuffMsg是放到发送缓冲区去发送,而且第一次用SendBuffMsg 时,才会去创建Write协程。如果这两个混用可能会出现些很难重现的时序问题。我觉得是不是去掉一个,或在 znix.json 里开个开关,去打开或关闭 buffer send 的功能。从程序逻辑上来说应该都用 Send Buffer 的。那怕把缓存区改小。因为工作协程是固定,如果遇到有几个网络连接不稳定的,可能把整个系统拖慢了。而且不看代码很难发现这个问题。 @aceld

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions