You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go语言Channel无数据传输、触发死锁问题排查

Go简易事件驱动框架Channel异常、死锁问题排查

问题根因

两个异常本质是4处基础用法错误导致:

  • 核心错误:events/setup.go中仅声明了Publish chan string通道变量,未执行初始化操作。Go语言中channel的零值为nil,对nil通道执行发送、接收操作会永久阻塞,这也是死锁报错中chan send (nil chan)、chan receive (nil chan)提示的直接来源。
  • for+select逻辑错误:带default分支的select在通道无就绪数据时会直接执行default分支逻辑,原代码在default分支中调用time.Sleep(math.MaxInt64)相当于让主协程永久睡眠,后续就算通道有数据写入,也不会再被select监听,因此程序会一直输出等待提示。
  • 通道遍历写法错误:for x := range <-events.Publish是错误写法,<-events.Publish是尝试从通道接收单个值,range会将接收到的string类型值作为遍历对象,既不符合通道遍历的语法逻辑,也会因为通道是nil直接阻塞在接收步骤触发死锁。
  • 附带参数传递bug:Trigger方法中调用事件处理函数时未将可变参数打散,导致多参数传入时会被额外包装一层切片,和预期传参逻辑不符。

修复方案

  • 初始化Publish通道:声明通道时直接通过make完成初始化,根据业务场景选择缓冲大小,本次场景3个事件各发送1条消息,初始化容量为3的带缓冲通道即可避免发送方不必要的阻塞。
  • 修正通道读取逻辑:删除default分支中永久睡眠的错误逻辑,使用标准for range语法遍历通道,通道关闭后循环会自动退出。
  • 修正WaitGroup用法:Wg.Add调用必须放在启动子goroutine之前,避免Add和Wait产生竞态;单独起协程等待所有事件处理完成后主动关闭Publish通道,通知接收方所有数据发送完毕。
  • 修正可变参数传递:调用事件处理Action时,对payload参数加...打散,匹配可变参数的传参要求。

修正后完整代码

events/setup.go

//events/setup.go
package events

import "sync"

var Wg sync.WaitGroup
// 声明时直接初始化带缓冲通道
var Publish = make(chan string, 3)

type Event struct {
	Name   string
	Action func(...any)
}

type Events struct {
	handlers []Event
}

func (e *Events) register(name string, action func(...any)) {
	e.handlers = append(e.handlers, Event{
		Name:   name,
		Action: action,
	})
}

func (e *Events) Trigger(name string, payload ...any) {
	for _, event := range e.handlers {
		if event.Name == name {
			// 可变参数打散后传入处理函数
			event.Action(payload...)
		}
	}
}

events/user.go

//events/user.go
package events

import "fmt"

var User Events

func init() {
	User.register("new", func(payload ...any) {
		fmt.Println(payload[0])
		Publish <- "{'new':'done'}"
		Wg.Done()
	})

	User.register("name", func(payload ...any) {
		for index, person := range payload {
			fmt.Println(person, index)
		}
		Publish <- "{'name':'done'}"
		Wg.Done()
	})
}

main.go

//main.go
package main

import (
	"edriven/events"
	"fmt"
)

func main() {
	fmt.Println("Starting")
	// Wg.Add放在goroutine启动前调用,避免竞态
	events.Wg.Add(3)
	go events.User.Trigger("new", "Hasan")
	go events.User.Trigger("name", []any{"Hasan", "Ali"})
	go events.User.Trigger("new", "Ali")

	// 等待所有事件处理完成后关闭通道
	go func() {
		events.Wg.Wait()
		close(events.Publish)
	}()

	// 标准range遍历通道,通道关闭后自动退出
	for x := range events.Publish {
		fmt.Println(x)
	}
}

修复后程序可正常输出所有事件处理结果,无死锁、通道阻塞问题。


内容的提问来源于stack exchange,提问作者Hasan A Yousef

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 03:12:18