例程的协调和正常关闭

时间:2019-04-28 15:54:17

标签: go concurrency

我对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()
}

2 个答案:

答案 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()
}