Понимание изящного канала в Go - PullRequest
0 голосов
/ 16 апреля 2019

В этой статье с сайта Go101 Я прочитал один трюк с двойным выбором для канала stopCh (в разделе «2. Один получатель, N отправителей, единственный получатель говорит« пожалуйста, прекратите отправлять больше »закрыв дополнительный сигнальный канал ").

Не могли бы вы описать, как это работает, и мне действительно нужно использовать его в реальных приложениях?

UPD : Я не спрашивал о закрытии канала.Я спросил об использовании этой части кода:

        // The try-receive operation is to try
        // to exit the goroutine as early as
        // possible. For this specified example,
        // it is not essential.
        select {
        case <- stopCh:
            return
        default:
        }

        // Even if stopCh is closed, the first
        // branch in the second select may be
        // still not selected for some loops if
        // the send to dataCh is also unblocked.
        // But this is acceptable for this
        // example, so the first select block
        // above can be omitted.
        select {
        case <- stopCh:
            return
        case dataCh <- rand.Intn(Max):
        }

Каков реальный вариант использования для двойного выбора stopCh?

1 Ответ

2 голосов
/ 16 апреля 2019

Ключевым моментом здесь является понимание того, как ведет себя выборка, если может выполняться несколько случаев, а именно псевдослучайно:

Если одно или несколько сообщений может продолжаться, одно из возможных сообщений выбирается с помощью равномерного псевдослучайного выбора.

https://golang.org/ref/spec#Select_statements

select {
case <- stopCh:
    return
case dataCh <- rand.Intn(Max):
}

Только с этим вторым оператором выбора после закрытия stopCh возможно, что оба случая могут продолжаться, если хотя бы одно из следующих условий верно:

  1. dataCh буферизуется и являетсяне в емкости
  2. по крайней мере одна попытка получения от dataCh, даже после закрытия stopCh

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

Если оба эти условия могут быть положительно исключены, первое утверждение select может быть опущено, поскольку невозможнооба случая готовы продолжить.В статье Go101 просто показано решение, которое гарантированно работает, не делая никаких предположений.

Этот шаблон не является редкостью в реальном коде и обычно связан с отменой контекста:

func f(ctx context.Context, ch chan T) {
    for {
        // Make sure we don't shoot after ctx has been
        // canceled, even if a target is already lined up.
        select {
        case <-ctx.Done():
            return
        default:
        }

        // Or, equivalently: if ctx.Err() != nil { return }


        select {
        case <-ctx.Done():
            return
        case t := <-ch:
            launchMissileAt(t)
        }
    }
}
...