从同一通道读取多个 Go 例程

我遇到了控制通道(某种)问题。

我的程序的本质:

  • 我不知道我将在运行时运行多少个 go 例程

  • 我需要在设定的时间重新启动这些 go 例程,但是,它们也可能出错(然后重新启动),因此它们的时间将无法预测。

  • 这些 go 例程会将消息放到单个通道上。

所以我所做的是创建一个简单的随机消息生成器,将消息放到一个通道上。当计时器启动时(测试的随机持续时间),我将一条消息放到一个控制通道上,该通道是一个结构有效载荷,所以我知道有一个关闭信号,并且它是例行公事;实际上,在再次开始Go例程之前,我会做一些我需要做的其他事情。

我的问题是:

  • 我在反射中收到控制消息。选择循环

  • 我没有(或无法)在我的randmsgs()循环中收到它

因此,我无法阻止我的randmsgs()去例行公事。

我相信我理解多个go例程可以从单个通道读取是正确的,因此我认为我误解了如何反映。选择大小写适合所有这些内容。

我的代码:

package main


import (

    "fmt"

    "math/rand"

    "reflect"

    "time"

)


type testing struct {

    control bool

    market  string

}


func main() {

    rand.Seed(time.Now().UnixNano())

    // explicitly define chanids for tests.

    var chanids []string = []string{"GR I", "GR II", "GR III", "GR IV"}

    stream := make(chan string)

    control := make([]chan testing, len(chanids))

    reflectCases := make([]reflect.SelectCase, len(chanids)+1)


    // MAKE REFLECT SELECTS FOR 4 CONTROL CHANS AND 1 DATA CHANNEL

    for i := range chanids {

        control[i] = make(chan testing)

        reflectCases[i] = reflect.SelectCase{Dir: reflect.SelectRecv, Chan: reflect.ValueOf(control[i])}

    }

    reflectCases[4] = reflect.SelectCase{Dir: reflect.SelectRecv, Chan: reflect.ValueOf(stream)}

    

    // START GO ROUTINES

    for i, val := range chanids {

        runningFunc(control[i], val, stream, 1+rand.Intn(30-1))

    }


    // READ DATA

    for {

        o, recieved, ok := reflect.Select(reflectCases)

        if !ok {

            fmt.Println("You really buggered this one up...")

        }

        ty, err := recieved.Interface().(testing)

        if err == true {

            fmt.Printf("Read from: %v, and recieved close signal from: %s\n", o, ty.market)

            // close control & stream here.

        } else {

            ty := recieved.Interface().(string)

            fmt.Printf("Read from: %v, and recieved value from: %s\n", o, ty)

        }


    }

}

我为文字墙道歉,但我完全没有想法:(!

DIEA
浏览 57回答 1
1回答

慕斯709654

下半部分的频道与前半部分的频道相同。qcontrol[0...3]您正在运行的通道也会从所有这些通道读取,而不会出现延迟。reflect.Select我认为问题归结为您的运行速度太快并立即“窃取”所有通道输出。这就是为什么永远无法读取消息的原因。reflect.Selectrandmsgs您会注意到,如果从 中删除默认大小写,则该函数能够(可能)从 中读取一些消息。randmsgsq&nbsp; &nbsp; &nbsp; &nbsp; select {&nbsp; &nbsp; &nbsp; &nbsp; case <-q:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println("Just sitting by the mailbox. :(")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return&nbsp; &nbsp; &nbsp; &nbsp; }这是因为现在它正在无延迟地运行,它总是在等待消息,因此有机会在比赛中击败。qreflect.Select如果您从多个 goroutine 的同一通道读取,则传递的数据将简单地转到首先读取的任何 goroutine。这个程序似乎只是一个实验/学习经验,但我会提供一些可能有帮助的批评。同样,通常,如果两个goroutine执行不同的任务,则您不会有多个goroutine从同一通道读取。您正在创建一个大多数非确定性的竞赛,即哪个goroutine首先获取数据。其次,这是一个常见的初学者的反模式,你应该避免选择:for {&nbsp; &nbsp; select {&nbsp; &nbsp; case v := <-myChan:&nbsp; &nbsp; &nbsp; &nbsp; doSomething(v)&nbsp; &nbsp; default:&nbsp; &nbsp; &nbsp; &nbsp; // Oh no, there wasn't anything! Guess we have to wait and try again.&nbsp; &nbsp; &nbsp; &nbsp; time.Sleep(time.Second)}此代码是多余的,因为其行为方式已经达到这样的状态,即如果最初没有准备好任何案例,它将等到任何案例准备就绪,然后继续处理该案例。这有效地使您的选择循环变慢,但实际在通道上等待的时间更少(因为99.999...%的时间都花在 )。selectdefault: sleeptime.Sleep
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go