Golang中channel无法接收数据或出现死锁问题如何解决

Golang中channel无法接收数据或出现死锁问题如何解决 我有以下代码:

//main.go
package main

import (
	"edriven/events"
	"fmt"
	"math"
	"time"
)

func main() {
	fmt.Println("Starting")
	events.Wg.Add(1)
	go events.User.Trigger("new", "Hasan")
	events.Wg.Add(1)
	go events.User.Trigger("name", []any{"Hasan", "Ali"})
	events.Wg.Add(1)
	go events.User.Trigger("new", "Ali")

	//for x := range <-events.Publish {
	//	fmt.Println(x)
	//}

	for {
		select {
		case x := <-events.Publish:
			fmt.Println(x)
		default:
			fmt.Println("waiting for data ...")
			time.Sleep((time.Duration(math.MaxInt64)))
		}
	}
}

以及

//events/user.go
package events

import "fmt"

var User Events

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

	})

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

以及

//events/setup.go
package events

import "sync"

var Wg sync.WaitGroup
var Publish chan string

/*var Publish chan map[string]string

func init() {
	Publish = make(chan map[string]string)
}
*/
type Event struct {
	Name   string
	Action func(...any) // <-chan string // func(...any) ([]any, error)

}

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)
		}
	}
}

我得到的输出如下所示,这表明没有任何数据通过通道进行交换:

enter image description here

如果我将 for { select {} } 循环替换为 for x := range <-events.Publish { } 循环,那么我会得到以下错误:

PS D:\Deployment\event-driven> go run edriven
Starting
[Ali]
[Hasan]
[[Hasan Ali]] 0
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive (nil chan)]:
main.main()
        D:/Deployment/event-driven/main.go:17 +0x1c5

goroutine 6 [chan send (nil chan)]:
edriven/events.init.0.func1({0xc000086010?, 0x1?, 0x1?})
        D:/Deployment/event-driven/events/user.go:12 +0x6c
edriven/events.(*Events).Trigger(0x0?, {0xe45ca0, 0x3}, {0xc000086000, 0x1, 0x1})
        D:/Deployment/event-driven/events/setup.go:34 +0x129
created by main.main
        D:/Deployment/event-driven/main.go:11 +0xb5

goroutine 7 [chan send (nil chan)]:
edriven/events.init.0.func2({0xc000180010?, 0x1?, 0x1?})
        D:/Deployment/event-driven/events/user.go:23 +0x45
edriven/events.(*Events).Trigger(0x0?, {0xe45db9, 0x4}, {0xc000180000, 0x1, 0x1})
        D:/Deployment/event-driven/events/setup.go:34 +0x129
created by main.main
        D:/Deployment/event-driven/main.go:13 +0x15d

goroutine 8 [chan send (nil chan)]:
edriven/events.init.0.func1({0xc000050260?, 0x1?, 0x1?})
        D:/Deployment/event-driven/events/user.go:12 +0x6c
edriven/events.(*Events).Trigger(0x0?, {0xe45ca0, 0x3}, {0xc000050250, 0x1, 0x1})
        D:/Deployment/event-driven/events/setup.go:34 +0x129
created by main.main
        D:/Deployment/event-driven/main.go:15 +0x1aa
exit status 2
PS D:\Deployment\event-driven>

更多关于Golang中channel无法接收数据或出现死锁问题如何解决的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于Golang中channel无法接收数据或出现死锁问题如何解决的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


问题在于 events.Publish 通道未初始化。在 events/setup.go 中,Publish 被声明为 chan string,但未使用 make 创建,导致通道为 nil。向 nil 通道发送或接收数据都会导致死锁。

以下是修复后的代码:

// events/setup.go
package events

import "sync"

var Wg sync.WaitGroup
var Publish chan string

func init() {
    Publish = make(chan string) // 初始化通道
}

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)
        }
    }
}

同时,main.go 中的无限循环需要调整,以避免阻塞并正确等待所有 goroutine 完成:

// main.go
package main

import (
    "edriven/events"
    "fmt"
    "sync"
)

func main() {
    fmt.Println("Starting")
    var wg sync.WaitGroup

    wg.Add(1)
    go func() {
        defer wg.Done()
        events.User.Trigger("new", "Hasan")
    }()

    wg.Add(1)
    go func() {
        defer wg.Done()
        events.User.Trigger("name", []any{"Hasan", "Ali"})
    }()

    wg.Add(1)
    go func() {
        defer wg.Done()
        events.User.Trigger("new", "Ali")
    }()

    // 启动一个 goroutine 来接收通道数据
    go func() {
        for x := range events.Publish {
            fmt.Println("Received:", x)
        }
    }()

    wg.Wait()
    close(events.Publish) // 关闭通道,结束接收循环
}

如果希望保持原有的 WaitGroup 设计,可以修改 events/user.go 中的 init 函数,确保 User 被正确初始化:

// events/user.go
package events

import "fmt"

var User Events

func init() {
    User = Events{} // 初始化 User
    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()
    })
}

主要修改点:

  1. events/setup.goinit 函数中初始化 Publish 通道。
  2. events/user.goinit 函数中初始化 User 变量。
  3. main.go 中使用 sync.WaitGroup 等待所有触发操作完成,然后关闭通道。
回到顶部