Кодосмотр: Делитесь памятью, общаясь
Когда программа запускается, она выделяет один Resource для каждого URL. Главный goroutine и goroutines Poller обмениваются Resource через каналы.
doc/codewalk/urlpoll.go:60,64
Функция Poller
Каждый Poller получает указатели на Resource из входного канала.
В этой программе принято, что отправка указателя Resource по каналу передаёт владение данными от отправителя получателю.
Благодаря этому соглашению мы знаем, что никакие два goroutine не будут одновременно обращаться к одному и тому же Resource.
Это означает, что нам не нужно заботиться о блокировках для предотвращения одновременного доступа к этим структурам данных.
Poller обрабатывает Resource, вызывая его метод Poll.
Он отправляет значение State в канал статуса, чтобы сообщить StateMonitor о результате Poll.
Наконец, он отправляет указатель Resource в канал out. Это можно интерпретировать как то, что Poller говорит: «Я закончил с этим Resource» и возвращает владение им обратно главному goroutine.
Несколько goroutine запускают Poller'ы, обрабатывая Resource параллельно.
Poller обрабатывает Resource, вызывая его метод Poll.
Он отправляет значение State в канал статуса, чтобы сообщить StateMonitor о результате Poll.
Наконец, он отправляет указатель Resource в канал out. Это можно интерпретировать как то, что Poller говорит: «Я закончил с этим Resource» и возвращает владение им обратно главному goroutine.
Несколько goroutine запускают Poller'ы, обрабатывая Resource параллельно.
doc/codewalk/urlpoll.go:86,92
Метод Poll
Метод Poll (типа Resource) выполняет HTTP HEAD-запрос для URL Resource и возвращает код статуса HTTP-ответа.
Если происходит ошибка, Poll выводит сообщение в стандартный поток ошибок и возвращает строку ошибки.
doc/codewalk/urlpoll.go:66,77
Функция main
Функция main запускает goroutine Poller и StateMonitor,
а затем циклически передаёт завершённые Resource обратно в канал pending после соответствующих задержек.
doc/codewalk/urlpoll.go:94,116
Создание каналов
Сначала main создаёт два канала типа *Resource, pending и complete.
Внутри функции main новый goroutine отправляет один Resource на каждый URL в канал pending, а главный goroutine получает завершённые Resource из канала complete.
Внутри функции main новый goroutine отправляет один Resource на каждый URL в канал pending, а главный goroutine получает завершённые Resource из канала complete.
doc/codewalk/urlpoll.go:95,96
Ожидающие и завершившиеся каналы передаются каждой из горутин Poller, внутри которых они известны как in и out.
doc/codewalk/urlpoll.go:95,96
Инициализация StateMonitor
StateMonitor инициализирует и запускает горутину, которая хранит состояние каждого Resource. Эта функция будет рассмотрена подробно позже.
Пока что важно отметить, что она возвращает канал типа State, который сохраняется как status и передаётся горутинам Poller.
Пока что важно отметить, что она возвращает канал типа State, который сохраняется как status и передаётся горутинам Poller.
doc/codewalk/urlpoll.go:98,99
Запуск горутин Poller
Теперь, когда у функции main есть необходимые каналы, она запускает несколько горутин Poller, передавая каналы в качестве аргументов.
Каналы обеспечивают средства связи между главной горутиной, горутинами Poller и StateMonitor.
doc/codewalk/urlpoll.go:101,104
Отправка Resource в pending
Чтобы добавить начальную работу в систему, main запускает новую горутину,
которая выделяет и отправляет один Resource на каждый URL в канал pending.
Новая горутина необходима, потому что отправка и получение по не буферизованным каналам являются синхронными. Это означает, что такие отправки блокируются до тех пор, пока Poller не будет готов читать из pending.
Если бы эти отправки выполнялись в главной горутине с меньшим количеством Poller, чем отправок в канал, программа бы попала в состояние взаимной блокировки, поскольку main ещё не начала получать из complete.
Упражнение для читателя: измените эту часть программы так, чтобы она читала список URL из файла. (Можно переместить эту горутину в отдельную именованную функцию.)
Новая горутина необходима, потому что отправка и получение по не буферизованным каналам являются синхронными. Это означает, что такие отправки блокируются до тех пор, пока Poller не будет готов читать из pending.
Если бы эти отправки выполнялись в главной горутине с меньшим количеством Poller, чем отправок в канал, программа бы попала в состояние взаимной блокировки, поскольку main ещё не начала получать из complete.
Упражнение для читателя: измените эту часть программы так, чтобы она читала список URL из файла. (Можно переместить эту горутину в отдельную именованную функцию.)
doc/codewalk/urlpoll.go:106,111
Главный цикл событий
Когда Poller завершает работу с Resource, он отправляет его в канал complete.
Этот цикл получает указатели на Resource из complete.
Для каждого полученного Resource запускается новая горутина, вызывающая метод Sleep у Resource.
Использование отдельной горутины для каждого Resource гарантирует, что сны могут происходить параллельно.
doc/codewalk/urlpoll.go:113,115
Обратите внимание, что любой единственный указатель на Resource может быть отправлен либо в pending, либо в complete только в один момент времени. Это гарантирует, что Resource либо обрабатывается горутиной Poller, либо находится в режиме сна, но никогда одновременно в обоих состояниях. Таким образом, мы разделяем данные Resource через коммуникацию.
doc/codewalk/urlpoll.go:113,115
Метод Sleep
Sleep вызывает time.Sleep для паузы перед отправкой Resource в done.
Пауза будет либо фиксированной длины (pollInterval), плюс дополнительная задержка, пропорциональная количеству последовательных ошибок (r.errCount).
Это пример типичного идиомы Go: функция, предназначенная для выполнения внутри горутины, принимает канал, на который она отправляет своё возвращаемое значение (или другое указание о завершённом состоянии).
Это пример типичного идиомы Go: функция, предназначенная для выполнения внутри горутины, принимает канал, на который она отправляет своё возвращаемое значение (или другое указание о завершённом состоянии).
doc/codewalk/urlpoll.go:79,84
StateMonitor
StateMonitor получает значения State по каналу и периодически выводит состояние всех Resource, которые обрабатываются программой.
doc/codewalk/urlpoll.go:32,50
Канал updates
Переменная updates — это канал типа State, на который горутины Poller отправляют значения State.
Этот канал возвращается функцией.
Этот канал возвращается функцией.
doc/codewalk/urlpoll.go:36
Карта urlStatus
Переменная urlStatus — это карта URL-адресов и их последних статусов.
doc/codewalk/urlpoll.go:37
Объект Ticker
time.Ticker — это объект, который повторно отправляет значение по каналу с заданным интервалом.
В данном случае ticker вызывает печать текущего состояния в стандартный вывод каждые updateInterval наносекунд.
В данном случае ticker вызывает печать текущего состояния в стандартный вывод каждые updateInterval наносекунд.
doc/codewalk/urlpoll.go:38
Горутина StateMonitor
StateMonitor будет работать вечно, выполняя выборку из двух каналов:
ticker.C и update. Инструкция select блокируется до тех пор, пока одно из
её сообщений не будет готово к выполнению.
Когда StateMonitor получает сигнал от ticker.C, он вызывает функцию logState для вывода текущего состояния. Когда же он получает обновление состояния от updates, он записывает новое состояние в карту urlStatus.
Обратите внимание, что эта горутина владеет структурой данных urlStatus, обеспечивая тем самым последовательный доступ к ней. Это предотвращает проблемы с повреждением памяти, которые могут возникнуть при параллельном чтении и/или записи общей карты.
Когда StateMonitor получает сигнал от ticker.C, он вызывает функцию logState для вывода текущего состояния. Когда же он получает обновление состояния от updates, он записывает новое состояние в карту urlStatus.
Обратите внимание, что эта горутина владеет структурой данных urlStatus, обеспечивая тем самым последовательный доступ к ней. Это предотвращает проблемы с повреждением памяти, которые могут возникнуть при параллельном чтении и/или записи общей карты.
doc/codewalk/urlpoll.go:39,48
Заключение
В этом руководстве мы рассмотрели простой пример использования примитивов параллелизма Go
для совместного использования памяти через коммуникацию.
Это должно стать отправной точкой для изучения различных способов, в которых горутины и каналы могут быть использованы для написания выразительных и лаконичных конкурентных программ.
Это должно стать отправной точкой для изучения различных способов, в которых горутины и каналы могут быть использованы для написания выразительных и лаконичных конкурентных программ.
doc/codewalk/urlpoll.go
Не общайтесь, разделяя память; разделяйте память, общаясь.
Каналы позволяют передавать ссылки на структуры данных между горутинами. Если рассматривать это как передачу владения данными (возможность их читать и записывать), то они становятся мощным и выразительным механизмом синхронизации.
В этом кодосмотре мы рассмотрим простую программу, которая опрашивает список URL-адресов, проверяет их HTTP-коды ответа и периодически выводит их состояние.
Pollers отправляют значения State в StateMonitor, который поддерживает карту текущего состояния каждого URL.