Почему эта реализация канала OrDone получает дважды от канала Done? - PullRequest
0 голосов
/ 02 марта 2020

Читаем через Concurrency in Go и вводим удобный "или готовый" канал.

TLDR; когда вы работаете с каналом, которым вы не управляете (предположительно из какой-то другой части вашей системы), код может стать немного уродливым.

// Quite nice to read
for v := range myChan {
  ... Do Stuff
}

// Not so nice
loop:
for {
  select {
  case <- done:
    break loop
  case maybeVal, ok := <-myChan:
    if !ok {
      return
    }
    // Do something with maybeVal
  }
}

Книга предлагает способ упростить это с помощью канала OrDone. Определяется следующим образом. Что я не понимаю, так это то, почему во вложенном выборе нужно снова получать от <-done. </p>

orDone := func(done, c <-chan interface{}) <-chan interface{} {
  valStream := make(chan interface{})
  go func() {
    defer close(valStream)
    for {
      select {
      case <- done:
        return
      case v, ok := <-c:
        if !ok {
          return
        }
        select {
        case valStream <- v:
        case <-done: // Why do we also need to receive on done here?
        }
      }
    }
  }
  return valStream
}

Это позволяет вам go вернуться к вашему оригиналу для l oop, улучшение читабельности - вот так:

for val := range orDone(done, myChan) {
  // Once again, do something
}

Ответы [ 3 ]

2 голосов
/ 02 марта 2020

Действительно просто добавление видимости к ответу Питера.

Это потому, что сам valStream может заблокировать отправку, если тот, кто получает valStream, теряет интерес.

1 голос
/ 02 марта 2020

В ситуации, когда вы получили значение из канала c, вы вводите в свой вложенный select{}. В этот момент, если мы уберем второй <-done, у вас будет это;

select {
case valStream: <- v:
}

Это будет блокировать на неопределенный срок, пока значение не будет получено из канала v, даже если канал done закрыт. Добавляя вложенную проверку, мы позволяем себе выйти из select{} в любой точке.

0 голосов
/ 02 мая 2020

Дополнительный вопрос, почему тело этого "case <-done:" пусто? </p>

orDone := func(done, c <-chan interface{}) <-chan interface{} {
  valStream := make(chan interface{})
  go func() {
    defer close(valStream)
    for {
      select {
      case <- done:
        return
      case v, ok := <-c:
        if !ok {
          return
        }
        select {
        case valStream <- v:
        case <-done: 
            return  // I think there should be a "return" here
        }
      }
    }
  }
  return valStream
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...