Сказка о бармене и сусликах

Часть I: История бармена

День в местном баре…

🚪🚶 ………… Мужчина заходит в бар.

🤵🏽📣👨 … Мужчина просит у бармена место за барной стойкой.

⏳ ……………. Мужчина ждет, пока бармен освободит место.

🤵🏽📣👨 … Бармен освобождает место.

🥃🥂 ………. Мужчина наслаждается напитками с некоторым весельем.

👨🏽📣🤵🏽 … Закончив, он прощается и уходит.

👋🏽 ……………. Бармен кивает и говорит: «До свидания».

🚪🚶 ………… Другой мужчина заходит в бар…

Это очень необычно для бара, но позвольте себе приостановить недоверие.

Вы видите, что это был очень маленький бар. Бар на одно место. Иногда много людей приходят одновременно, чтобы занять свободное место в баре. Бармен разговаривает со всеми, пропуская по одному. Это имело бы смысл и сработало бы, если бы люди стояли в очереди в порядке их прибытия и если бы все соблюдали правила. К сожалению, в этом баре все было не так. Люди ломали очереди.

🏃🏽‍♂️ … 🪑 . 🤵🏽 . 🚪 🚶🏽‍♂️🚶🏽‍♂️🚶🏽‍♂️🚶🏽‍♂️

Бармену было бы проще просто объявить о наличии свободных мест и предоставить толпе решать, кто следующий будет пить. Проблема в том, что всегда было два или более человека, которые одновременно претендовали на трон. Это привело к дракам в барах с дедлоками и людьми, использующими слово на М. Это расстроило бармена. Все это насилие и ненормативная лексика не шли на пользу бизнесу. Это все усложняло.

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

Он вывесил приведенные ниже правила возле бара.

  1. 🚪🚶 ………… Любой мужчина может подойти к двери.
  2. 👨🏽📣🤵🏽 … Любой мужчина в баре может попросить место за барной стойкой.
  3. ⏳ ……………. Мужчины должны ждать, пока бармен освободит место.
  4. 🤵🏽📣👨🏽 … Мужчин пускают внутрь только с разрешения.*
  5. 🥃🥂 ………… Мужчинам нужно время, чтобы насладиться напитками.
  6. 👨🏽📣🤵🏽 … Мужчины должны сообщить об этом, когда закончат в баре.

* Настоящим заявлено, что как только освободится место, бармен свернет носовой платок и вытолкнет его из отверстия в двери так, чтобы один его конец свисал наружу. Первый человек за дверью, которому удастся схватиться за этот конец, может вытащить его и предъявить как разрешение войти в бар.

🏃🏽‍♂️ … 🪑 . 🤵🏽 ⬜ 🚪 🚶🏽‍♂️🚶🏽‍♂️🚶🏽‍♂️🚶🏽‍♂️

Это сработало, ну вроде. В баре драк не было. Это блокировало посетителей и сдерживало конфликты снаружи. Конечно, не всем меценатам пришлось нелегко. Некоторые из них продолжали блокироваться VIP-персонами, прежде чем они могли добраться до носового платка. Некоторые из них устали, и им пришлось вздремнуть. Из-за этого они несколько раз упускали свой шанс. Их жизнь не сильно изменилась. Это был просто новый набор правил. Их жизни вращались вокруг двери бара, как будто они были прикованы к ней. М-слова произносились, но не вслух и не открыто.

Трудности бармена усугублялись и в других отношениях. Чтобы сдержать свое обещание и придерживаться правил, он оказался заблокированным у двери даже в нерабочее время. Он должен был стоять там, держа носовой платок, даже если снаружи никого не было. Он не мог заниматься никакой другой работой, пока кто-нибудь не оторвал от него платок.

~ ▪ ~ дышать ~ ▪ ~

Часть II: Барный заговор

А потом появились суслики…

Однажды поздно ночью бармен крепко спал, держась за ноющую руку. Его разбудил пронзительный писк нескольких сусликов, пробравшихся в бар. Он принял их за обыкновенных крыс и хотел размозжить дубинкой. Но когда он поднял палку, суслики заговорили. Они умоляли его о пощаде. Они заверили его, что не будут требовать от него многого и будут мирно сосуществовать. Они даже обещали ему милости взамен, готовы были стать его рабами. В баре знали о его проблемах. Бармен уронил свою палку и улыбнулся, пока суслики продолжали детализировать соглашение. Подобные пронзительные скрипы были слышны по всему городу. Когда утром взошло солнце, мир был немного другим.

Суслики заключили сделку со всеми людьми. Они обещали служить им, выполняя всю их работу по дому. Мало ли люди знали, во что они ввязываются. Люди полностью приняли это безоговорочно. Суслики не просто выполняли черную и выжидательную работу. Они заменили людей везде, даже в баре. Это они выпивали и ждали у дверей вместо бармена. Бедный бармен удалился на чердак, суслики отвлекли его. Приходили к нему группами, чтобы сообщить, как идут дела. Каждый раз, когда он получал все свои обновления, он обычно уставал и возвращался в постель.

Это была единственная вещь, которую суслики не могли сделать для людей. Они не могли спать за людей. Так что люди продолжали жить в мире сусликов. Ждут сусликов и спят. ✨

🦡 … 🪑 . 🦡 ⬜ 🚪 🦡🦡🦡🦡

~ ▪ ~ дышать ~ ▪ ~

Часть III: параллелизм баров

Мы можем перевести приведенную выше историю в код с помощью Go. Но сначала …

Небольшое введение в каналы Go

Два способа чтения из канала и один способ записи в него

// read a value from a channel
value <- someChannel
// read from a channel and ignore the value
<- someChannel
// write a value into a channel
someChannel <- "some value"

Каналы Go похожи на дыру в двери

  • Если вы пишете в канал, вы будете заблокированы, пока кто-то не прочитает из него
  • Если вы читаете с канала, вы будете заблокированы, пока кто-то не напишет в него

О горутинах

Go предоставляет ключевое слово go для выполнения функций в отдельной горутине.

name := "Norm"
// will spawn a separate thread and unblock the main thread
go func() {
  fmt.Println(name)
}

Горутины можно рассматривать как отдельные потоки

  • Их также можно использовать для вызова встроенных функций.
  • Go поддерживает замыкания

Время собрать все воедино…

// create a channel
holeInTheDoor := make(chan string)
// make availability known when the bar opens (don't block)
go func ()  {
  // 🤵🏽📣👨🏽👨🏽👨🏽
  holeInTheDoor <- "seating available"
}()
func (m Man) drinkAtTheBar()  {
  go func() {
    // ⏳ wait until the seat is available
    <-m.holeInTheDoor
    // Do the actual drinking here 🍺🥂
    // 👨🏽📣🤵 announce done drinking
    m.drinkingEnded <- "drinking ended"
  }
  go func ()  {
    // ⏳ wait until drinking has ended
    <-m.drinkingEnded
    // 🤵🏽📣👨🏽👨🏽👨🏽 announce seating available
    holeInTheDoor <- "seating available"
  }()
}

Теперь мы можем позволить им всем пить

for _, m := range men {
  m.drinkAtTheBar()
}

Примечание.

  • drinkingEnded — это еще один канал, который используется для сообщения о том, что пьянство закончилось. Если на этом канале будет получено сообщение, мы можем сообщить людям о наличии мест.
  • Мы не можем предсказать, кто будет обслужен первым и когда. Мы можем быть уверены, что правила будут соблюдены и в бар будет допущен только один человек.
  • Вышеприведенный цикл for завершится быстро, так как функция drinkAtTheBar просто порождает горутины и возвращается.
  • Нам понадобится какой-то механизм, чтобы родительский поток ждал, пока выпьют все посетители. Это можно сделать с помощью sync.WaitGroup, но мы опустим его, чтобы сделать пример короче и сосредоточиться на каналах.

~ ▪ ~ смотреть вдаль ~ ▪ ~

Часть IV: Повышение планки

Что, если бар расширит количество сидячих мест с 1 до 4? Просто, мы используем буферизованный канал длиной 4 и изначально пишем в него 4 сообщения.

  • Буферизованные каналы могут содержать до 4 сообщений.
  • Это означает, что мы можем начать с 4 сообщений, что позволит 4 людям начать пить.
  • По мере завершения сеансов выпивки в канал будут отправляться новые сообщения.
  • Из-за ограничения емкости в любой момент времени в буфере канала не может быть более 4 сообщений.
// create a buffered channel of length 4
holeInTheDoor := make(chan string, 4)
// make availability known when the bar opens (don't block)
go func ()  {
  // 🤵🏽📣👨🏽👨🏽👨🏽
  holeInTheDoor <- "seating available"
  holeInTheDoor <- "seating available"
  holeInTheDoor <- "seating available"
  holeInTheDoor <- "seating available"
}()

Теперь предположим, что бармену не нравится затишье в баре с 16 до 17 часов, и он хочет увеличивать толпу у дверей каждый день в этот отрезок времени. Возможно, он может объявить, что купоны будут распределяться случайным образом между 16:00 и 17:00. Если кто-то будет присутствовать в момент выдачи купонов, он сможет забрать купоны. Он не хочет блокировать сусликов, потому что чувствует, что уже переутомляет их.

Мы можем пойти с предложением select и создать неблокирующую запись канала. Если на couponChannel никто не ждет, купон будет утерян.

// create a buffered channel of length 4
couponChannel := make(chan string)
// make availability known when the bar opens (don't block)
go func ()  {
  // 🤵🏽📣 💸💸
  select {
    case couponChannel <- "You've won $50!":
      fmt.Println("coupon claimed!")
    default:
      fmt.Println("coupon lost!")
  }
}()

А еще есть люди, которые не слишком привязаны к бару. Они могут не захотеть ждать. Они просто хотят быстро заглянуть, и если места нет, они захотят перейти к другим своим жизненным приоритетам.

select также можно использовать для неблокирующего чтения каналов, как показано ниже. Здесь, если в holeInTheDoor нет сообщения, код перейдет к случаю по умолчанию.

func (m Man) tryDrinkAtTheBar()  {
  select {
    case  <-holeInTheDoor:
      fmt.Println("do the drink!")
    default:
      fmt.Println("imma outta here!")
  }
}()

Хм, так мы затронули ниже в этой статье

  • горутины
  • каналы go — небуферизованные и буферизованные
  • чтение из каналов — блокирующее и неблокирующее чтение
  • запись в каналы — блокирующая и неблокирующая запись

И с этим я тоже убираюсь отсюда! Спасибо, что были со мной через это. Беречь себя!