我对Go语言的学习完全陌生(目前我在使用频道和Go语言例程,我正在尝试理解这种协调)。我陷入了一个问题。
当我开始执行2 go例程时,我不了解幕后发生的事情。 ServiceAlpha的责任是向ServiceBeta发出心跳信号并在中止通道上发生中止事件时死亡 ServiceBeta的责任是将一些愚蠢的字符串写入我的日志输出中 并且它有一个TTL(来自serviceAlpha),因此,如果它在TTL时间内没有心跳,则应完成其工作并返回给他的呼叫者,但在退出之前,应在频道终止时给ServiceAlpha一些终止事件>
我的问题我不明白为什么在TTL和
之后它仍然可以运行输出看起来像这样:
2019/04/28 17:21:44 Main | START
2019/04/28 17:21:44 serviceBeta | START
2019/04/28 17:21:44 serviceAlpha | START
2019/04/28 17:21:44 serviceAlpha | There was not abort
2019/04/28 17:21:44 serviceAlpha | rand = 3
2019/04/28 17:21:45 serviceBeta | TICKER: 2019-04-28 17:21:45.2870594 +0200 CEST m=+1.003883901
2019/04/28 17:21:46 serviceBeta | TICKER: 2019-04-28 17:21:46.2867957 +0200 CEST m=+2.003620201
2019/04/28 17:21:47 serviceBeta | TICKER: 2019-04-28 17:21:47.2866242 +0200 CEST m=+3.003448701
2019/04/28 17:21:47 serviceAlpha | There was not abort
2019/04/28 17:21:47 serviceAlpha | rand = 5
2019/04/28 17:21:48 serviceBeta | TICKER: 2019-04-28 17:21:48.2869918 +0200 CEST m=+4.003816301
2019/04/28 17:21:49 serviceBeta | TICKER: 2019-04-28 17:21:49.2863265 +0200 CEST m=+5.003151001
2019/04/28 17:21:50 serviceBeta | TICKER: 2019-04-28 17:21:50.2868071 +0200 CEST m=+6.003631601
2019/04/28 17:21:51 serviceBeta | TICKER: 2019-04-28 17:21:51.2866738 +0200 CEST m=+7.003498301
2019/04/28 17:21:51 serviceBeta | ABORT 2019-04-28 17:21:51.2866738 +0200 CEST m=+7.003498301
Keeps running...
missing serviceBeta: STOP
and ServiceAlpha,and Main also
我很确定这段代码有很多问题,所以我的问题是 -这个设计有什么问题? :) -如何正确编码?
我们将不胜感激!
Darvi
package main
import (
"log"
rand2 "math/rand"
"sync"
"time"
)
func main() {
log.Println("Main | START")
var wg sync.WaitGroup
var resetTTL = make(chan interface{})
var abort = make(chan interface{})
defer func() {
log.Println("Main | defer closing channels")
close(resetTTL)
close(abort)
}()
wg.Add(1)
go serviceAlpha(1, 5, resetTTL, abort, &wg)
wg.Add(1)
go serviceBeta(4*time.Second, resetTTL, abort, &wg)
wg.Wait()
log.Println("Main | STOP")
}
func serviceAlpha(min, max int, ttlReset chan<- interface{}, abort <-chan interface{}, wg *sync.WaitGroup) {
log.Println("serviceAlpha | START")
var randTTL int
loop:
for {
select {
case <-abort:
log.Println("serviceAlpha | There was an abort, breaking from loop")
break loop
default:
log.Println("serviceAlpha | There was not abort")
break
}
randTTL = rand2.Intn(max-min) + min + 1
log.Printf("serviceAlpha | rand = %v", randTTL)
time.Sleep(time.Duration(randTTL) * time.Second)
ttlReset <- true
}
log.Println("serviceAlpha | STOP")
wg.Done()
}
func serviceBeta(ttl time.Duration, ttlReset <-chan interface{}, abort chan<- interface{}, wg *sync.WaitGroup) {
log.Println("serviceBeta | START")
var ttlTimer = time.NewTimer(ttl)
var tickerTimer = time.NewTicker(1 * time.Second)
loop:
for {
select {
case <-ttlReset:
ttlTimer.Stop()
ttlTimer.Reset(ttl)
case tt := <-tickerTimer.C:
log.Println("serviceBeta | TICKER: ", tt)
case ttl := <-ttlTimer.C:
log.Println("serviceBeta | ABORT", ttl)
break loop
}
}
abort <- true
log.Println("serviceBeta | STOP")
wg.Done()
}
答案 0 :(得分:0)
我刚刚弄清楚了! :) Rubber_duck_debugging
中止需要容量1;),并且在尝试重置TTL时应同时检查中止
var abort = make(chan interface{}, 1)
func serviceAlpha(min, max int, ttlReset chan<- interface{}, abort <-chan interface{}, wg *sync.WaitGroup) {
log.Println("serviceAlpha | START")
var randTTL int
loop:
for {
randTTL = rand2.Intn(max-min) + min + 1
log.Printf("serviceAlpha | rand = %v", randTTL)
time.Sleep(time.Duration(randTTL) * time.Second)
select {
case <-abort:
log.Println("serviceAlpha | There was an abort, breaking from loop")
break loop
default:
log.Println("serviceAlpha | There was not any abort event")
ttlReset <- true
break
}
}
log.Println("serviceAlpha | STOP")
wg.Done()
}
答案 1 :(得分:0)
尽管您的解决方案可以在这种情况下工作,但这仍然是设计缺陷。您原始代码中的问题是有效的死锁,其中serviceAlpha
试图写入ttlReset
,而serviceBeta
不再监听该频道。这会阻止serviceAlpha
。现在,serviceBeta
想写到abort
,但是serviceAplha
当前不在监听,这也阻塞了serviceBeta
。使用缓冲通道是可行的,因为它不再受阻。
但是,我可以使用代码代替睡眠,并使它工作。这是serviceAlpha
的修改代码:
func serviceAlpha(min, max int, ttlReset chan<- interface{}, abort <-chan interface{}, wg *sync.WaitGroup) {
log.Println("serviceAlpha | START")
var randTTL int
loop:
for {
randTTL = rand2.Intn(max-min) + min + 1
log.Printf("serviceAlpha | rand = %v", randTTL)
ticker := time.NewTicker(time.Duration(randTTL) * time.Second)
select {
case <-abort:
log.Println("serviceAlpha | There was an abort, breaking from loop")
break loop
case <-ticker.C:
log.Println("serviceAlpha | There was not abort")
ttlReset <- true
}
}
log.Println("serviceAlpha | STOP")
wg.Done()
}