Зарегистрируйтесь, чтобы продолжить обучение

Горутины Основы Go

Вот и подошло время познакомиться с самой сильной стороной языка 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

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

Также мы одновременно объявили и тут же вызвали горутину, так же как мы можем объявить и вызвать функцию.


Аватары экспертов Хекслета

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

Вам ответят команда поддержки Хекслета или другие студенты

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»
Изображение Тото

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