Вы нашли 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 горутин, но они совсем не используются, они просто блокируют другие горутины, которые выполняют реальную работу.
Вывод
Мы должны начать обращать внимание на наш код и время выполнения функций, потому что есть большая вероятность, что мы запускаем что-то, что может выполняться быстрее! 🚗 (красные машины самые быстрые)