Go ticker пример не выбирает «готово» дело? - PullRequest
0 голосов
/ 07 октября 2019

Я адаптировал этот пример, https://gobyexample.com/tickers, к следующему сценарию:

package main

import (
    "fmt"
    "time"
)

func main() {
    ticker := time.NewTicker(500 * time.Millisecond)
    done := make(chan bool)

    go func() {
        for {
            select {
            case <-done:
                fmt.Println("Received 'done'")
                return
            case t := <-ticker.C:
                fmt.Println("Tick at", t)
            }
        }
    }()

    time.Sleep(1600 * time.Millisecond)
    // ticker.Stop()
    done <- true
    // fmt.Println("Ticker stopped.")
}

Два отличия от приведенного примера в том, что я закомментировал строку ticker.Stop() и добавилfmt.Println("Received 'done'") строка в блоке case <-done. Если я запускаю это, я наблюдаю следующий вывод:

> go run tickers.go
Tick at 2019-10-06 15:25:50.576798 -0700 PDT m=+0.504913907
Tick at 2019-10-06 15:25:51.074993 -0700 PDT m=+1.003102855
Tick at 2019-10-06 15:25:51.576418 -0700 PDT m=+1.504521538

Мой вопрос: почему он не печатает Received 'done' на терминал?

Странно, если я комментирую в Ticker stopped Println, я тоже вижу Received 'done':

> go run tickers.go
Tick at 2019-10-06 15:27:30.735163 -0700 PDT m=+0.504666656
Tick at 2019-10-06 15:27:31.234076 -0700 PDT m=+1.003573649
Tick at 2019-10-06 15:27:31.735342 -0700 PDT m=+1.504833296
Ticker stopped.
Received 'done'

Насколько я помню, код в Goroutine может работать синхронно, поэтому я озадачен тем, что не вижуэффект оператора Println в первом случае, так как это происходит до возвращения Goroutine. Может кто-нибудь объяснить это?

Ответы [ 2 ]

2 голосов
/ 07 октября 2019

Когда done <- true произошло, основная функция вернулась напрямую.

Вы можете добавить другое время. Sleep (), чтобы увидеть, что произошло.

    time.Sleep(1600 * time.Millisecond)
    // ticker.Stop()
    done <- true
    // fmt.Println("Ticker stopped.")
    time.Sleep(1600 * time.Millisecond)
1 голос
/ 07 октября 2019

... почему он не печатает полученное 'выполнено' на терминал?

Это делает - или, скорее, пытается .

Программа Go завершается, когда возвращается основная программа (которая называется main пакета main) (или раньше, в некоторых случаях, не встречающихся здесь). Ваш main возвращается после вызова time.Sleep() и последующей отправки true на done.

Между тем, процедура, находящаяся в цикле for, пробуждается, когда значение true поступает в done канал. Это происходит после того, как основная программа отправила его, после чего основная программа находится в процессе выхода.

Если в этом процессе основная программа занимает достаточно много времени, анонимная программа будет иметь время дляпечать Received 'done'. Если в этом процессе основной процедуры достаточно быстро, анонимная программа никогда не завершает или, возможно, даже не начинает печатать что-либо, и вы ничего не видите. (Фактический вывод выполняется одним базовым системным вызовом, поэтому вы либо получаете все это, либо ничего из этого.)

Вы можете убедиться, что процедура, которую вы выделили, завершается, прежде чем ваш основной выход завершится любымколичество механизмов, но простейшим, вероятно, является использование sync.WaitGroup, поскольку он как бы предназначен для этого. Создайте группу ожидания, установите ее счетчик на 1 (добавьте 1 к ее начальному нулю), а затем вызовите функцию Done на выходе из анонимной программы. Попросите главную программу ожидания подождать.

См. Пример игровой площадки Go .

...