go буферный канал (емкость 4) принимает 5 без блокировки - PullRequest
0 голосов
/ 04 августа 2020

У меня есть код go, который в основном выполняет две подпрограммы go одновременно. Один из них отправляет 10 чисел типа int из (1-10) в буферизованный канал «ch» (емкость 4), а другой go программа считывает значения из канала с помощью for range loop

  package main

  import (
      "fmt"
      "sync"
      "time"
      "runtime"
  )

  func doSm(ch chan int, wg *sync.WaitGroup) {
      defer wg.Done()

      for i := 1; i <= 10; i++ {
          fmt.Println("sending", i)
          ch <- i
          fmt.Println("sent", i)
      }

      close(ch)
  }

  func doSm2(ch chan int, wg *sync.WaitGroup) {
      defer wg.Done()

      time.Sleep(5 * time.Second)
      for v := range ch {
          fmt.Println("result:", v)
      }
  }

  func main() {
      runtime.GOMAXPROCS(1)
      var wg sync.WaitGroup
      ch := make(chan int, 4)

      wg.Add(2)
      go doSm(ch, &wg)
      go doSm2(ch, &wg)
      wg.Wait()
  }

Проблема может быть обнаружена в выходных данных ниже.

doSm () отправляет 4 int через канал, а go планировщик блокирует процедуру go, пока doSm2 () не считывает эти 4 значения из канала. После этого буфер пуст и doSm () отправляет 1 int, а doSm2 () мгновенно его считывает. Теперь буфер снова пуст и готов к отправке 4 значений. Однако doSm () каким-то образом отправляет 5 значений (6, 7, 8, 9, 10), несмотря на свою емкость.

sending 1
sent 1
sending 2
sent 2
sending 3
sent 3
sending 4
sent 4
sending 5
result: 1
result: 2
result: 3
result: 4
result: 5
sent 5
sending 6
sent 6
sending 7
sent 7
sending 8
sent 8
sending 9
sent 9
sending 10
sent 10
result: 6
result: 7
result: 8
result: 9
result: 10

Есть идеи, почему это происходит? Или мне чего-то не хватает?

Ответы [ 2 ]

2 голосов
/ 04 августа 2020

Пример объяснения.

result: 5
sent 5
sending 6
sent 6
sending 7
sent 7
sending 8
sent 8
sending 9
sent 9
sending 10
sent 10
result: 6

result 5 напечатано, затем мы видим sent 6-7-8-9-10, затем мы видим напечатанное result 6. Это не означает, что все значения 6-7-8-9-10 находятся в буфере канала (очевидно, что это не так). Значение 6 уже получено из канала, но следующая строка fmt.Println() еще не выполнена. Но поскольку получено 6, в буфере есть только 3 числа, и поэтому 10 может быть отправлено по каналу, как видно на выходе.

2 голосов
/ 04 августа 2020

Вот возможное выполнение, которое вызовет этот вывод:

  • Отправка горутины отправляет 4 значения и печатает
  • Отправка блоков горутины
  • Получение горутины получает одно значение
  • Отправка горутины отправляет другое значение и распечатывает
  • Получение отпечатков горутины
  • Получение горутины получает оставшиеся значения

При этом чередовании он выглядит как хотя отправка горутины отправила 5 значений.

Вкратце: когда задействовано несколько горутин, печать в стандартный вывод может не показать фактическое чередование горутин.

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