通道未接收到数据或出现死锁问题

huangapple go评论71阅读模式
英文:

Channel is not recieving data or giving dead block

问题

我已经翻译好了你提供的代码部分,如下所示:

//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", []interface{}{"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 ...interface{}) {
		fmt.Println(payload[0])
		//message := make(map[string]string)
		//message["new"] = "done new"
		Publish <- "{'new':'done'}"
		Wg.Done()

	})

	User.register("name", func(payload ...interface{}) {
		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

type Event struct {
	Name   string
	Action func(...interface{}) // <-chan string // func(...any) ([]any, error)
}

type Events struct {
	handlers []Event
}

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

func (e *Events) Trigger(name string, payload ...interface{}) {
	for _, event := range e.handlers {
		if event.Name == name {
			event.Action(payload...)
		}
	}
}

你提供的代码中,通过通道进行的数据交换没有成功。如果你将 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>

希望这些翻译对你有帮助!如果你有任何其他问题,请随时提问。

英文:

I've the below code:

//main.go
package main

import (
	&quot;edriven/events&quot;
	&quot;fmt&quot;
	&quot;math&quot;
	&quot;time&quot;
)

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

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

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

And

//events/user.go
package events

import &quot;fmt&quot;

var User Events

func init() {
	User.register(&quot;new&quot;, func(payload ...any) {
		fmt.Println(payload[0])
		//message := make(map[string]string)
		//message[&quot;new&quot;] = &quot;done new&quot;
		Publish &lt;- &quot;{&#39;new&#39;:&#39;done&#39;}&quot;
		Wg.Done()

	})

	User.register(&quot;name&quot;, func(payload ...any) {
		for index, person := range payload {
			fmt.Println(person, index)
		}
		//message := make(map[string]string)
		//message[&quot;name&quot;] = &quot;done name&quot;
		Publish &lt;- &quot;{&#39;name&#39;:&#39;done&#39;}&quot; //message
		Wg.Done()
	})
}

And

//events/setup.go
package events

import &quot;sync&quot;

var Wg sync.WaitGroup
var Publish chan string

type Event struct {
	Name   string
	Action func(...any) // &lt;-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)
		}
	}
}

The output I got is as below, that is nothing is exchanged through the channels

通道未接收到数据或出现死锁问题

If I replaced the for { select {} } loop by the for x := range &lt;-events.Publish { } loop then I get the below error:

PS D:\Deployment\event-driven&gt; 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&gt; 

答案1

得分: 2

这段代码有问题:

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

select被调用并且假设Publish通道仍然为空时,default语句将运行并使用time.Sleep语句永远阻塞主循环。因此,即使Publish通道从另一个go例程接收到数据,主go例程仍然被卡在Sleep语句上。

每当你想要将定时等待与通道事件结合时,可以这样做:

timerChannel := time.NewTimer(duration)

select {
case <-timerChannel.C:
    {
        // 超时
    }
case x := <-events.Publish:
    {
        fmt.Println(x)
    }
}

但是,由于你的意图似乎只是阻止main退出,那么更简单的方法是:

for {
    x := <-events.Publish  // 阻塞直到Publish通道有数据
    fmt.Println(x)
}

但正如你指出的,这会导致死锁,因为在你的三个go例程退出后,没有其他任务可执行。

快速修复:

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

    exitChannel := make(chan bool)
    go func() {
        events.Wg.Wait()
        close(exitChannel)
    }()

    canExit := false
    for !canExit {
        select {
        case x := <-events.Publish:
            {
                fmt.Println(x)
            }
        case <-exitChannel:
            {
                canExit = true
            }
        }
    }
}

如评论中所讨论的,需要初始化通道,缺少了一个make,应该这样做:

package events

import "sync"

var (
    Wg      sync.WaitGroup
    Publish chan string
)

func init() {
    Publish = make(chan string)
}
英文:

This block of code is problematic

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

When select is invoked and assuming the Publish channel is still empty, the default case will run and block the main loop forever with the time.Sleep statement. Hence, even if the Publish channel receives data from another go-routine, the main go-routine is still stuck on that Sleep statement.

Any time you want to combine a timed wait with a channel event, you can do this:

timerChannel := time.NewTimer(duration)

select {
case &lt;-timerChannel.C:
	{
        // time out
	}
case x := &lt;-events.Publish:
	{
		fmt.println(x)
	}
}

But since your intent appears to just block main from exiting, then it's even simpler:

for {
    x := &lt;- events.Publish:  // blocks until Publish channel has data
    fmt.Println(x)
}

But as you called out, that leads to a deadlock because after your three go-routines after exited, there's nothing left to do.

Quick fix:

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

    exitChannel := make(chan bool)
    go func() {
        events.Wg.Wait()
        close(exitChannel)
    }()

    canExit := false
    for !canExit {
        select {
        case x := &lt;-events.Publish:
            {
                fmt.Println(x)
            }
        case &lt;- exitChannel:
            {
                canExit = true
            }
        }
    }
}

As discussed in the comments, channel is required to be initialized, a make is missing, it has to be done as:

package events

import &quot;sync&quot;

var (
	Wg      sync.WaitGroup
	Publish chan string
)

func init() {
	Publish = make(chan string)
}

huangapple
  • 本文由 发表于 2022年7月11日 04:22:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/72931632.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定