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

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

package main

type coder struct {
   id int
}

const availableCoders = 1

var job = 500

func main() {

}

Мы создали основную структуру со структурой coder, которая представляет программиста, есть также константа availableCoders, которая будет представлять, как Многие программисты могут помочь с проектом, и есть проектная переменная, которая показывает, сколько часов программирования необходимо потратить, чтобы добиться цели.

В реальном сценарии нам нужно нанять несколько программистов, так что давайте сделаем это! Нам нужно создать какое-то место, где будут ждать все доступные программисты (которым нечем заняться). Лучший способ сделать это - создать канал.

var (
   project = 500
   waitingCoders chan coder
)

func main() {

}

func init() {
   waitingCoders = make(chan coder, availableCoders)
}

func employeeCoders(codersAmount int, wCoders chan coder) {
   for i := 0; i < codersAmount; i++ {
      wCoders <- coder{id: i}
   }
}

Мы добавили новую переменную var expectCoders, которая является каналом типа кодировщика. Мы также ввели функцию инициализации, которая просто создаст этот канал при инициализации этого пакета, и мы сделали эту функцию employeeCoders (), которая, как видно из названия, служащая новым кодировщикам (создает новые кодеры, которые отправляются на буферизованный канал ожиданияCoders). ВАЖНО: waitCoders - это буферный канал, чтобы избежать взаимоблокировок в нашем сценарии.

Так что давайте сотрудникам-кодерам и дадим им работу! 💻

func doCodingStuff(cd coder) {
   time.Sleep(10 * time.Microsecond)
   project-- // programmer works for one hour
   fmt.Printf("Coder %d coded for 2 seconds! Job left: %d\n", cd.id, project)
}
func main() {
   go employeeCoder(availableCoders, waitingCoders)

   for {
      w := <-waitingCoders
      doCodingStuff(w)
      waitingCoders <- w
   }
}

Мы добавили функцию doCodingStuff, которая ждет 10 микросекунд (чтобы имитировать какое-то задание), а затем вычитает 1 из проекта.

Подождите, что-то не так, мы забыли создать какой-то оператор break, когда проект будет завершен! Таким образом, мы можем просто добавить простой оператор break, когда значение проекта равно или меньше 0.

func main() {
   go employeeCoder(availableCoders, waitingCoders)

   for {
      if project <= 0 {
         break
      }

      w := <-waitingCoders
      go func() {
   doCodingStuff(w)
   waitingCoders <- w
}()
   }

   println("\nProject done!\n")
}

Теперь все в порядке:

Но мы хотим измерить время, поэтому давайте добавим код для измерения времени выполнения. Кроме того, нам нужно добавить горутину внутри цикла for для одновременного выполнения кода.

func main() {
   start := time.Now()

   go employeeCoder(availableCoders, waitingCoders)

   for {
      if project <= 0 {
         break
      }

      w := <-waitingCoders
      go func() {
         doCodingStuff(w)
         waitingCoders <- w
      }()
   }

   println("\nProject done!\n")

   elapsed := time.Since(start)
   log.Printf("Main took %s\n", elapsed)
}

Нет, давайте измерять

+------------------+----------------+
| Amount of coders | Execution time |
+------------------+----------------+
| 1                | 7.6634362s     |
+------------------+----------------+
| 10               | 767.9652ms     |
+------------------+----------------+
| 100              | 65.9979ms      |
+------------------+----------------+
| 1000             | 2.0032ms       |
+------------------+----------------+
| 10000            | 1.9996ms       |
+------------------+----------------+
| 100000           | 3.0014ms       |
+------------------+----------------+
| 1000000          | 14.9998ms      |
+------------------+----------------+

Что ж, для 1 кодера время выполнения составляет ~ 7,5 с, а для 10 000 ~ 2 мсек, а затем идет 100 000, которые должны идти… быстрее? Не делает, почему 🐌?
Ну, во-первых, наша задача действительно небольшая, это просто вычитание чисел. Во-вторых, создается и выделяется где-то от 10 000 до 100 000 горутин, но они совсем не используются, они просто блокируют другие горутины, которые выполняют реальную работу.

Вывод

Мы должны начать обращать внимание на наш код и время выполнения функций, потому что есть большая вероятность, что мы запускаем что-то, что может выполняться быстрее! 🚗 (красные машины самые быстрые)