我有一个案例,我正在从两个不同的位置(ES和REDIS)读取数据,我需要从最快的源读取一个值,因此我发射2个goroutines,一个从ES获取数据,另外从REDIS获得它。
一旦从其中一个goroutine中取出数据,必须完全取消另一个goroutine而不是浪费CPU。
简化为:
func A(){
go funcB(){
}()
go funcC(){
}()
data := <-channel //
}
现在收到数据后,funcA
或funcB
必须取消,无论他们在做什么(我不再关心他们的输出,他们只是在浪费CPU)
最有效的方法是什么? 可以只使用频道吗?
答案 0 :(得分:6)
context package为此目的提供取消,超时和截止日期上下文。在这里您可以看到取消示例,我们等待较慢的goroutine打印已取消的消息:
ctx, cancel := context.WithCancel(context.Background())
// buffer the channel for extra results returned before cancelation
data := make(chan string, 2)
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
select {
case <-time.After(100 * time.Millisecond):
data <- "A complete"
case <-ctx.Done():
fmt.Println("A cancelled")
}
}()
wg.Add(1)
go func() {
defer wg.Done()
select {
case <-time.After(200 * time.Millisecond):
data <- "B complete"
case <-ctx.Done():
fmt.Println("B cancelled")
}
}()
resp := <-data
cancel()
fmt.Println(resp)
wg.Wait()
答案 1 :(得分:2)
根据您的实际使用情况,您有一些选项:
1-使用两个goroutines:
这需要sync/Lock
:
试试这个模拟样本(The Go Playground):
package main
import (
"fmt"
"math/rand"
"sync"
"time"
)
func main() {
rand.Seed(time.Now().Unix())
time.AfterFunc(time.Duration(rand.Intn(1000))*time.Millisecond, func() { ES <- 101 })
time.AfterFunc(time.Duration(rand.Intn(1000))*time.Millisecond, func() { REDIS <- 102 })
go B()
go C()
data := <-channel
fmt.Println(data)
}
func B() {
check := true
data := 0
for {
select {
case <-quit:
return
case data = <-ES: // receive data
}
if check {
mx.Lock()
//defer mx.Unlock()
if mx.done {
mx.Unlock()
return
}
check = false
close(quit)
mx.done = true
mx.Unlock()
}
fmt.Println("ES ready")
channel <- data
}
}
func C() {
check := true
data := 0
for {
select {
case <-quit:
return
case data = <-REDIS: // receive data
}
if check {
mx.Lock()
//defer mx.Unlock()
if mx.done {
mx.Unlock()
return
}
check = false
close(quit)
mx.done = true
mx.Unlock()
}
fmt.Println("REDIS ready")
channel <- data
}
}
var (
channel = make(chan int)
ES = make(chan int)
REDIS = make(chan int)
quit = make(chan struct{})
mx lockdown
)
type lockdown struct {
sync.Mutex
done bool
}
2-在此示例中,您只需启动一个goroutine B
或C
:
看到这个伪代码:
func main() {
go A()
data := <-channel
fmt.Println(data)
}
func A() {
for{
if ES ready
go B(data)
return
if REDIS ready
go C(data)
return
}
}
您可以在A
goroutine中启动A
goroutine,它会检测哪个输入已准备就绪,例如ES
或REDIS
,然后相应地启动B
或C
goroutine:
尝试此模拟样本(The Go Playground):
AfterFunc仅用于模拟,在实际代码中你不需要它,它模拟一个输入的随机时序。
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
rand.Seed(time.Now().Unix())
time.AfterFunc(time.Duration(rand.Intn(1000))*time.Millisecond, func() { ES <- 101 })
time.AfterFunc(time.Duration(rand.Intn(1000))*time.Millisecond, func() { REDIS <- 102 })
go A()
data := <-channel
fmt.Println(data)
}
func A() {
select {
case data := <-ES:
go B(data)
return
case data := <-REDIS:
go C(data)
return
}
}
func B(data int) {
for {
fmt.Println("ES ready")
channel <- data
data = <-ES
}
}
func C(data int) {
for {
fmt.Println("REDIS ready")
channel <- data
data = <-REDIS
}
}
var (
channel = make(chan int)
ES = make(chan int)
REDIS = make(chan int)
)
运行1的输出:
REDIS ready
102
运行2的输出:
ES ready
101