Вот и подошло время познакомиться с самой сильной стороной языка Go — горутинами.
Что такое горутины? Горутины это функции (или методы), которые можно запускать параллельно с другими функциями или методами. Их также называют легковесными потоками, потому что они управляются рантаймом языка, а не операционной системой. Стоимость переключения контекста и расход памяти намного ниже, чем у потоков ОС. Следовательно, для Go — не проблема поддерживать одновременно десятки тысяч горутин.
Горутины "общаются" через каналы. Каналы предотвращают состояние гонки при доступе к общей памяти через горутины. Подробнее о каналах мы расскажем в следующих уроках.
Запустить функцию в горутине — супер легко. Для этого достаточно написать слово go
перед вызовом функции:
package main
import (
"fmt"
)
func concurrent() {
fmt.Println("Hello concurrent world")
}
func main() {
go concurrent()
fmt.Println("Hello main world")
}
Вывод:
Hello main world
Как видно, программа завершилась, а горутина так и не вывелась. Почему? При работе с горутинами важно знать несколько основных свойств:
-
Когда новая горутина запускается, программа не ждет ее завершения. Управление возвращается сразу после вызова горутины и переходит на новую строчку кода. Потому в примере выше горутина не успела выполниться, так как программа завершилась.
-
Функция
main()
выполняется в своей собственной main-горутине. Когда эта главная горутина завершается, то завершится программа и все другие горутины тоже.
Теперь, зная об особенностях горутин, допишем код:
package main
import (
"fmt"
"time"
)
func concurrent() {
fmt.Println("Hello concurrent world")
}
func main() {
go concurrent()
// добавим таймаут, чтобы горутина успела выполниться
time.Sleep(100 * time.Millisecond)
fmt.Println("Hello main world")
}
Вывод:
Hello concurrent world Hello main world
Теперь, благодаря таймауту, горутина успевает выполниться, программа и выводит обе строки.
При написании конкурентного кода возникают новые моменты, которые нужно учитывать: состояние гонки, блокировки, коммуникация между горутинами. Также нельзя полагаться на порядок выполнения горутин, так как они выполняются независимо друг от друга:
package main
import (
"fmt"
"time"
)
func main() {
for i := 0; i < 5; i++ {
go func() {
fmt.Println(i)
}()
}
time.Sleep(100 * time.Millisecond)
}
Вывод:
0 4 3 1 2
Можно заметить, что числа вывелись не в порядке вызова. Горутины выполняются независимо и не гарантируют порядка. При необходимости последовательность в выполнении придется реализовывать самостоятельно.
Также мы одновременно объявили и тут же вызвали горутину, так же как мы можем объявить и вызвать функцию.

Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты