Параллелизм в golang на пальцах
30 07 2018 admin 2 комментарияНекоторые концепции лучше осознавать при помощи аналогий.
Итак, goroutines (горутины) — подпрограммы, которые запускаются и начинают вести свою жизнь отдельно от вызвавшего их кода.
И вот близкий к жизни пример: компания по доставке пиццы, начинается очередной рабочий день, заходит менеджер, дает наставление в колцентр, не дожидается ответа и заходит в цех — благославляет поваров на работу, так же не ждёт от них ответа и идёт отдыхать. А в это время кипит работа — ребята клепают пиццы раз в секунду, как рабы на галерах:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
package main
import (
"fmt"
"time"
)
func main() {
//Будим отдел продаж, чтоб не спали
go receive_calls()
//Просим поваров начать готовить
go make_pizza()
fmt.Println("Менеджер выдал команды, можно отдохнуть!")
time.Sleep(time.Second*5)
}
func make_pizza() {
for {
fmt.Println("приготовили пиццу")
time.Sleep(time.Second)
}
}
func receive_calls() {
for {
fmt.Println("принимаем звонки")
time.Sleep(time.Second)
}
}
|
Результат запуска:
1
2
3
4
5
6
7
8
9
10
11
|
Менеджер выдал команды, можно отдохнуть!
принимаем звонки
приготовили пиццу
принимаем звонки
приготовили пиццу
принимаем звонки
приготовили пиццу
принимаем звонки
приготовили пиццу
принимаем звонки
приготовили пиццу
|
Это был пример, когда два процесса идут параллельно без всякой взаимосвязи (упрощенно, хотя, конечно, на самом деле она есть).
Так вот, когда параллельные процессы логически как-то связаны — их можно соединить каналом связи. И посылать сообщения от одного процесса к другому. При чём, в оба направления.
Небуферизированный канал
Рассмотрим наш цех подробнее. Представим, что он неплохо автоматизирован и там царит тотальное разделение труда. Первый повар раскатывает тесто, второй накладывает начинку, третий кладет в печь и забирает готовую пиццу, четвертый пакует продукцию. Повара — это горутины.
Представим, что они сообщаются напрямую. Вот раскатал парень лепешку, повернулся ко второму и тянет к нему — на держи. Это неплохой пример небуферизированного канала. Пока второй повар не возьмет лепешку у первого, первый так и будет держать её и не станет браться за новую пиццу. Как только второй соизволит принять — первый продолжит работу. Так и в обратном направлении. Допустим, что второй уже положил начинку и ему нужна новая основа — обращается к первому, а тот её только начал делать. Второй не сможет продолжить работу, пока первый не отдаст основу.
Буферизированный канал
Пример буферизированного канала — тот же цех, только между поварами ещё стоят столы и теперь они не тянут пиццу друг другу напрямую, а кладут на стол в ожидании, что следующий повар возьмет заготовку с неё. Ёмкость канала — количество мест на столе, допустим, равно двум. Представим, что паковщик пошёл пить чай. Теперь третий повар не остановится после первой пиццы, он сделает две, положит их на стол, сделает третью и остановится, потому что складывать уже некуда. Когда паковщик получит по шапке и прибежит на рабочее место, он сможет забирать по одной пицце со стола.
В go можно закрыть канал: приходит менеджер и говорит
— хорош, парни, пакуйте последнюю партию и расходимся.
А для уверенности подходит к столу с готовой пиццей (после третьего повара) и ставит перед ним табличку «больше пиццу не класть». В таком случае паковщик сможет получить последнюю партию в количестве 2-ух штук, но не больше. А третий повар при попытке подкинуть ещё одну штуку на стол получит от менеджера порицание в виде panic)
К слову, закрыть канал можно как со стороны отправителя, так и принимающего.
И причем тут параллелизм? Это конкуренция — concurrency.
concurrency и переводится как раз, как параллелизм. Откуда взята конкуренция?