Golang выбор оператора с каналами и группой ожидания - PullRequest
1 голос
/ 17 апреля 2020

Экспериментируя с Golang, я создал функцию с оператором select, которая слушает два канала.

Моя проблема в том, что код, кажется, ведет себя недетерминированно - иногда он паникует, а иногда и успешно завершается.

Я ожидаю, что этот код всегда должен вызывать пани c. Сначала он должен получить сообщение об ошибке, потому что его следует отправить до завершения waitGroup. и, следовательно, прежде чем канал успеха будет переведен на.

package main

import (
    "errors"
    "fmt"
    "sync"
)

func main() {
    errs := make(chan error, 1)
    success := make(chan bool, 1)

    doSomething(success, errs)

    select {
    case err := <-errs:
        fmt.Println("error", err)
        panic(err)
    case <-success:
        fmt.Println("success")
    }
    fmt.Println("finished successfully")
}

func doSomething(success chan bool, errs chan error) {
    var wg sync.WaitGroup
    wg.Add(1)
    go func() {
        defer wg.Done()
        err := errors.New("Some error")
        errs <- err
    }()

    wg.Wait()
    success <- true
}

1 Ответ

4 голосов
/ 17 апреля 2020

Оба канала готовы перед оператором выбора; поэтому он будет выбирать через равномерный псевдослучайный выбор :

Давайте заменим вызов функции doSomething в вашем коде и поместим defer в конец функции:

package main

import (
    "errors"
    "fmt"
    "sync"
)

func main() {
    errs := make(chan error, 1)
    success := make(chan bool, 1)

    var wg sync.WaitGroup
    wg.Add(1)
    go func() {
        err := errors.New("some error")
        errs <- err
        wg.Done()
    }()

    wg.Wait()
    success <- true

    select {
    case err := <-errs:
        fmt.Println("error", err)
        panic(err)
    case <-success:
        fmt.Println("success")
    }
    fmt.Println("finished successfully")
}

Как вы видите в приведенном выше примере кода, основная программа ожидает в wg.Wait() для wg.Done() в этот момент времени код (почти) функционально равен следующему код и оба канала готовы перед оператором select здесь:

package main

import (
    "errors"
    "fmt"
)

func main() {
    errs := make(chan error, 1)
    success := make(chan bool, 1)
    errs <- errors.New("some error")
    success <- true

    select {
    case err := <-errs:
        fmt.Println(err)
    case <-success:
        fmt.Println("success")
    }
}

Run:

$ go run .
some error

$ go run .
success

Select_statements :

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

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