首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >通道不是接收数据,也不是给死块。

通道不是接收数据,也不是给死块。
EN

Stack Overflow用户
提问于 2022-07-10 20:22:31
回答 1查看 53关注 0票数 0

我有以下代码:

代码语言:javascript
运行
复制
//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)))
        }
    }
}

代码语言:javascript
运行
复制
//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()
    })
}

代码语言:javascript
运行
复制
//events/setup.go
package events

import "sync"

var Wg sync.WaitGroup
var Publish chan 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)
        }
    }
}

我得到的输出如下所示,即没有通过通道进行交换。

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

代码语言:javascript
运行
复制
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> 
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-07-10 20:37:48

这段代码是有问题的

代码语言:javascript
运行
复制
for {
    select {
    case x := <- events.Publish:
        fmt.Println(x)
    default:
        fmt.Println("waiting for data ...")
        time.Sleep((time.Duration(math.MaxInt64)))
    }
}

当调用select并假设Publish通道仍然为空时,默认情况将运行,并使用time.Sleep语句永久阻塞主循环。因此,即使Publish通道接收到来自另一个go例程的数据,主要的go例程仍然被困在睡眠语句上。

当您想要将时间等待与通道事件组合起来时,您可以这样做:

代码语言:javascript
运行
复制
timerChannel := time.NewTimer(duration)

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

但是,由于您的意图似乎只是阻止main退出,那么它就更简单了:

代码语言:javascript
运行
复制
for {
    x := <- events.Publish:  // blocks until Publish channel has data
    fmt.Println(x)
}

但是正如你所呼吁的那样,这会导致一个僵局,因为在你退出后的三个例程之后,就没有什么可做的了。

快速修复:

代码语言:javascript
运行
复制
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")

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

正如注释中所讨论的,需要初始化通道,缺少一个制作,它必须执行如下操作:

代码语言:javascript
运行
复制
package events

import "sync"

var (
    Wg      sync.WaitGroup
    Publish chan string
)

func init() {
    Publish = make(chan string)
}
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72931632

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档