我有一个goroutine,它可以生成无数个值(每个值都比上一个更合适),但是找到每个值需要花费更长的时间。我试图找到一种方法来添加一个时间限制,比如10秒,之后我的函数会执行到目前为止收到的最佳值。
这是我当前的解决方案",使用频道和计时器:
// the goroutine which runs infinitely
// (or at least a very long time for high values of depth)
func runSearch(depth int, ch chan int) {
for i := 1; i <= depth; i++ {
fmt.Printf("Searching to depth %v\n", i)
ch <- search(i)
}
}
// consumes progressively better values until the channel is closed
func awaitBestResult(ch chan int) {
var result int
for result := range ch {
best = result
}
// do something with best result here
}
// run both consumer and producer
func main() {
timer := time.NewTimer(time.Millisecond * 2000)
ch := make(chan int)
go runSearch(1000, ch)
go awaitBestResult(ch)
<-timer.C
close(ch)
}
这主要起作用 - 在计时器结束并关闭通道后处理最佳结果。但是,然后我从panic: send on closed channel
goroutine中获得了恐慌(runSearch
),因为主要功能关闭了该频道。
如何在计时器完成后停止第一个goroutine运行?非常感谢任何帮助。
答案 0 :(得分:0)
你正在恐慌,因为你的发送goroutine runSearch
显然比计时器更长,并且它正在尝试在已经被main
goroutine关闭的频道上发送一个值。您需要设计一种方法来通知发送过程例程,一旦计时器失效并在关闭主要频道之前发送任何值。另一方面,如果您的搜索越早结束,您还需要与main
进行通信以继续。您可以使用一个频道并进行同步,以便不存在竞争条件。最后,您需要知道消费者何时处理了所有数据,然后才能退出main。
这可能有所帮助。
package main
import (
"fmt"
"sync"
"time"
)
var mu sync.Mutex //To protect the stopped variable which will decide if a value is to be sent on the signalling channel
var stopped bool
func search(i int) int {
time.Sleep(1 * time.Millisecond)
return (i + 1)
}
// (or at least a very long time for high values of depth)
func runSearch(depth int, ch chan int, stopSearch chan bool) {
for i := 1; i <= depth; i++ {
fmt.Printf("Searching to depth %v\n", i)
n := search(i)
select {
case <-stopSearch:
fmt.Println("Timer over! Searched till ", i)
return
default:
}
ch <- n
fmt.Printf("Sent depth %v result for processing\n", i)
}
mu.Lock() //To avoid race condition with timer also being
//completed at the same time as execution of this code
if stopped == false {
stopped = true
stopSearch <- true
fmt.Println("Search completed")
}
mu.Unlock()
}
// consumes progressively better values until the channel is closed
func awaitBestResult(ch chan int, doneProcessing chan bool) {
var best int
for result := range ch {
best = result
}
fmt.Println("Best result ", best)
// do something with best result here
//and communicate to main when you are done processing the result
doneProcessing <- true
}
func main() {
doneProcessing := make(chan bool)
stopSearch := make(chan bool)
// timer := time.NewTimer(time.Millisecond * 2000)
timer := time.NewTimer(time.Millisecond * 12)
ch := make(chan int)
go runSearch(1000, ch, stopSearch)
go awaitBestResult(ch, doneProcessing)
select {
case <-timer.C:
//If at the same time runsearch is also completed and trying to send a value !
//So we hold a lock before sending value on the channel
mu.Lock()
if stopped == false {
stopped = true
stopSearch <- true
fmt.Println("Timer expired")
}
mu.Unlock()
case <-stopSearch:
fmt.Println("runsearch goroutine completed")
}
close(ch)
//Wait for your consumer to complete processing
<-doneProcessing
//Safe to exit now
}
在playground上。更改timer
的值以观察两种情况。
答案 1 :(得分:0)
您需要确保goroutine知道何时完成处理,以便它不会尝试写入已关闭的通道,并发生恐慌。
这听起来像是context
包的完美案例:
func runSearch(ctx context.Context, depth int, ch chan int) {
for i := 1; i <= depth; i++ {
select {
case <- ctx.Done()
// Context cancelled, return
return
default:
}
fmt.Printf("Searching to depth %v\n", i)
ch <- search(i)
}
}
然后在main()
:
// run both consumer and producer
func main() {
ctx := context.WithTimeout(context.Background, 2 * time.Second)
ch := make(chan int)
go runSearch(ctx, 1000, ch)
go awaitBestResult(ch)
close(ch)
}