通道收到新值时将终止无限循环

问题描述 投票:1回答:3

我有2个模块(module1和module2),其中我在module1中生成一些随机数,并将其通过main中定义的通道传递给module2。当值进入模块2时,我希望将其循环打印,直到新的值再次从module1到达为止。这是代码。

[main.go文件内容

package main

import (
    "time"
    "example/module1"
    "example/module2"
)

func main() {
    ints := make(chan []int, 1)
    for {
        select {
        case <- time.After(5 * time.Second):
            go module1.GenerateRandint(ints)
        case  <- module2.Done:
            go module2.Start(ints)
        }
    }
}

module1的内容如下。

package module1

import (
    "math/rand"
    "time"
)

func GenerateRandint(a chan []int){
    var rint []int
    for  i:=0;i<1;i++ {
        rand.Seed(time.Now().UnixNano())
        rint = append(rint,  rand.Int())
    }
    a <- rint
}

module2是:

package module2

import (
    "fmt"
    "math/rand"
    "sync"
    "time"
)

var Done = make(chan struct{})
var quit = make(chan struct{})

type localData struct {
    nums []int
    Lock sync.RWMutex
}

var store localData

func init() {
    go func() {
        Done <- struct{}{}
        quit <- struct{}{}
    }()
    go printer(0, quit)
}

func printer(id int, quit chan struct{}) {

    for {
        select {
        case <-quit:
            return
        default:
            store.Lock.RLock()
            fmt.Println("ID:", id, "====", store.nums)
            time.Sleep(500 * time.Millisecond)
            store.Lock.RUnlock()

        }
    }
}

func Start(a chan []int) {
    rand.Seed(time.Now().UnixNano())
    d := <-a
    fmt.Println("RECEIVED DATA", d)
    store.Lock.Lock()
    go func() {
        quit <- struct{}{}
    }()
    store.nums = d
    store.Lock.Unlock()
    go printer(rand.Int(), quit)
    Done <- struct{}{}

}

在最终输出中,我看不到printer功能连续打印切片。它打印一次。我在下面粘贴示例输出。

ID: 0 ==== []
RECEIVED DATA [5183630848712612481]
ID: 2057228961822266542 ==== [5183630848712612481]
RECEIVED DATA [7203870927705193095]
RECEIVED DATA [1478549829208931483]
ID: 2311909806311895805 ==== [1478549829208931483]
RECEIVED DATA [3000658591728341557]

理想的输出应该是:

ID: 0 ==== []
RECEIVED DATA [5183630848712612481]
ID: 2057228961822266542 ==== [5183630848712612481]
ID: 2057228961822266542 ==== [5183630848712612481]
ID: 2057228961822266542 ==== [5183630848712612481]
ID: 2057228961822266542 ==== [5183630848712612481]
ID: 2057228961822266542 ==== [5183630848712612481]
RECEIVED DATA [7203870927705193095]
ID: 3052382001843759201 ==== [7203870927705193095]
ID: 3052382001843759201 ==== [7203870927705193095]
ID: 3052382001843759201 ==== [7203870927705193095]
ID: 3052382001843759201 ==== [7203870927705193095]
ID: 3052382001843759201 ==== [7203870927705193095]
RECEIVED DATA [1478549829208931483]
ID: 3850200927174591249 ==== [1478549829208931483]
ID: 3850200927174591249 ==== [1478549829208931483]
ID: 3850200927174591249 ==== [1478549829208931483]
ID: 3850200927174591249 ==== [1478549829208931483]
ID: 3850200927174591249 ==== [1478549829208931483]
RECEIVED DATA [3000658591728341557]
go channel
3个回答
0
投票

我假设您正在询问有关go printer(0, quit)函数中对init()的调用的问题。之所以只打印整个列表一次,是因为以下原因:

在init()函数中,您试图将数据发送到Donequit通道。现在,它们显然会阻塞,直到接收器准备就绪为止。装入模块后,接收器尚未准备好,因此您可以使用打印机方法进入default情况。因此,它将打印该语句一次,然后进入睡眠状态500毫秒。在此期间,main()启动并为Done通道创建一个接收器。这将取消阻止对该通道的写入,并随后也写入quit通道。现在,由于quit也有数据,因此printer方法中的select语句仅返回而不再打印。


0
投票

您无法杀死goroutine:没有killmurder功能。即使有一个,goroutine也没有name,也没有用户可访问的线程ID。因此,杀死一个特定 goroutine的唯一方法是谋杀all goroutine,例如通过os.Exit或从os.Exit返回的核爆炸摧毁整个世界。

goroutine can

可以通过从其初始函数返回或调用main正常退出,因此,您需要做的是安排循环在其停止处停止。自己的:
我有2个模块(module1和module2),其中我在module1中生成一些随机数,并将其通过main中定义的通道传递给module2。当这些值落入模块2中时,我希望将其循环打印,直到新的值再次从module1到达为止。]

您的实际代码看起来非常奇怪。例如,在Go中发信号通知某事“完成”通常是通过一个简单的

closed

通道来处理的,而不是在上面发送数据:runtime.Goexit
这特别有效,因为关闭事件实际上是广播给多个频道的读者:

runtime.Goexit

例如,您的doneChan := make(chan struct{})
// ...
close(doneChan) // tell everyone we are done
可以使用这种方法告诉

both

模块退出。此外,在我看来,逻辑安排将像这样(doneChan := make(chan struct{}) go f1(doneChan) go f2(doneChan) // ... go fN(doneChan) // ... close(doneChan) // tells f1, f2, ..., fN we are done )。我们从只有一个职责的生成函数开始:生成一片随机数并将其发送,在被告知退出时退出:

main

然后,我们有一个循环使用这些函数的函数,直到被告知退出为止。它还通过see complete Playground version指示实际完成的时间,以便其调用者可以告诉它真正完成的时间。请注意,我删除了所有全局变量和花式// it's not clear why `a` is `[]int` when there is only one
func GenerateRandints(a chan<- []int, done <-chan struct{}) {
    rand.Seed(time.Now().UnixNano())
    for {
        rint := []int{rand.Int()}
        select {
        case a <- rint:
        case <-done:
            return
        }
    }
}
函数以及互斥锁。大概在真实的代码中,它们有一定的用途,您可能需要将它们中的一些或全部放回原处。sync.WaitGroup函数,另一个具有随机数ID的函数。我保留了init并在此处简单地将零硬编码。)

Printer

最后,我们需要fmt.Println来驱动动作。我在这里补了一个;您真正的人无疑会有所不同:

func UseRandints(wg *sync.WaitGroup, a <-chan []int, done <-chan struct{}) {
    defer wg.Done()
    var r []int
    for {
        select {
        case r = <-a:
        case <-done:
            return
        default:
            if r != nil {
                fmt.Println("ID:", 0, "====", r)
            }
            time.Sleep(500 * time.Millisecond)
        }
    }
}


1
请注意,调用main与仅返回之间有一些细微的差异。将其转换为func main() { ints := make(chan []int) done := make(chan struct{}) go GenerateRandints(ints, done) var wg sync.WaitGroup wg.Add(1) go UseRandints(&wg, ints, done) // wait 5 seconds then tell everyone to quit <-time.After(5 * time.Second) close(done) // wait for UseRandints() to signal that it's done wg.Wait() } 可显示效果(尽管从技术上讲这里有比赛):

runtime.Goexit

[当该程序在Go操场上运行时,它等待一秒钟,打印complete program,然后以运行时异常(func main() { go f1() runtime.Goexit() } func f1() { time.Sleep(time.Second) fmt.Println("f1 finished") runtime.Goexit() } )终止,因为所有goroutine均已退出。
f1 finished:此程序立即退出,不打印fatal error: no goroutines (main called runtime.Goexit) - deadlock!,也没有任何运行时错误。这是因为调用Commenting out the runtime.Goexit() call in main produces a program that behaves differently意味着我们永远不会经历runtime.Goexit()返回所执行的“杀死所有其他goroutines”代码路径。

我通过修改main和module2解决了这个问题
main.go

main

Module2文件已更改为如下所示。

f1 finished

0
投票
main.go
© www.soinside.com 2019 - 2024. All rights reserved.