Горутины в Go: Базовые примеры
- вторник, 23 июля 2024 г. в 00:00:07
Горутины — это функции или методы, выполняемые конкурентно с другими горутинами в одном и том же адресном пространстве. Они легковеснее традиционных потоков, занимают меньше памяти и позволяют эффективно использовать ядра процессора.
Запуск горутины происходит с помощью ключевого слова go
, за которым следует вызов функции:
func sayHello() {
fmt.Println("Hello, world!")
}
func main() {
go sayHello()
time.Sleep(1 * time.Second) // Даем время горутине на выполнение
}
Давайте теперь запустим несколько горутин:
func sayHello(i int) {
fmt.Println("Hello, world! I'm", i, "goroutine!")
}
func main() {
for i := range 5 {
go sayHello(i)
}
time.Sleep(1 * time.Second) // Даем время горутинам на выполнение
}
Таким образом получим вывод:
Hello, world! I'm 4 goroutine!
Hello, world! I'm 0 goroutine!
Hello, world! I'm 1 goroutine!
Hello, world! I'm 2 goroutine!
Hello, world! I'm 3 goroutine!
Обратите внимание, что порядок выполнения горутин не гарантирован.
В примерах выше мы ожидали выполнение горутин с помощью time.Sleep(1 * time.Second)
в самом конце main()
Ожидание завершения горутин — критически важная часть управления конкурентными операциями в Go. Без явного ожидания горутин главный поток может завершиться раньше, чем горутины успеют выполнить свою работу, что приведет к тому, что горутины просто не выполнятся.
Пожалуй самый простой способ дождаться выполнения всех горутин это прибегнуть к sync.WaitGroup
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done()
fmt.Printf("Worker %d starting\n", id)
time.Sleep(time.Second)
fmt.Printf("Worker %d done\n", id)
}
func main() {
var wg sync.WaitGroup
for i := 1; i <= 5; i++ {
wg.Add(1)
go worker(i, &wg)
}
wg.Wait() // Ожидаем завершения всех горутин
}
Суть довольно проста:
wg.Add(1)
добавляет единицу к счетчику
wg.Done()
вычитает единицу из счетчика
wg.Wait()
ожидает когда счетчик будет равен нулю
Таким образом wg.Wait()
дождется пока каждая из горутин выполнит wg.Done()
Для коммуникации между горутинами чаще всего используют каналы.
func sayHello(i int, message chan string) {
message <- fmt.Sprint("Hello, world! I'm ", i, " goroutine!") // Отправляем сообщение в канал
}
func main() {
message := make(chan string) // Создаем канал для строк
for i := range 5 {
go sayHello(i, message)
}
for range 5 {
fmt.Println(<-message) // Читаем сообщения из канала
}
close(message)
}
В данном примере мы создали канал типа string
, породили 5 горутин, отправили в канал 5 сообщений и вывели все сообщения в терминал.
В примере нет необходимости использовать WaitGroup
так как мы ожидаем выполнения всех горутин при помощи механизма чтения из канала.
Эта статья представляет собой лишь верхушку айсберга возможностей горутин и каналов в Go, и служит отправной точкой для более углубленного изучения.