← Все этапы
05 · КОНКУРЕНТНОСТЬУрок 30 из 3318 минут

Горутины и замыкания

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

Запуск функции без ожидания

Ключевое слово go начинает вызов функции в отдельной горутине. Вызывающая функция продолжает работу, а сама горутина выполняется независимо. Конструкция работает также с анонимной функцией, то есть замыканием.

Обычный вызов функции дожидается её результата. Запуск через go не возвращает результат вызывающему коду, поэтому для обмена данными нужны канал, защищённое общее состояние или другая координация. Если main завершится, процесс тоже завершится: он не ждёт оставшиеся горутины автоматически.

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

Пример

package main

import (
	"fmt"
	"sync"
)

type reply struct {
	position int
	message  string
}

func main() {
	names := []string{"Ася", "Борис", "Вера"}
	replies := make(chan reply, len(names))
	var workers sync.WaitGroup

	for position, name := range names {
		workers.Add(1)
		go func(position int, name string) {
			defer workers.Done()
			replies <- reply{position: position, message: "готово: " + name}
		}(position, name)
	}

	go func() {
		workers.Wait()
		close(replies)
	}()

	ordered := make([]string, len(names))
	for reply := range replies {
		ordered[reply.position] = reply.message
	}
	for _, message := range ordered {
		fmt.Println(message)
	}
}

Основная функция передаёт позиции и имена в параметры каждой горутины. Канал достаточно вместителен для всех ответов, а после завершения отправителей координатор закрывает его. Основная функция читает до закрытия и сохраняет ответы по исходному индексу, поэтому завершение в любом порядке не меняет печать.

Передача параметров — ясный способ зафиксировать значения для конкретного вызова. Если вместо этого менять общую переменную из нескольких горутин, нужен mutex или передача сообщений через канал; иначе возникает гонка данных.

Практика

Попробуйте сами

Для списка слов ["дом", "лес", "море"] запусти отдельную горутину на каждое слово. Каждая должна посчитать число байтов в строке и вернуть индекс вместе с ответом. В основной функции напечатай результаты в исходном порядке.

Проверка результата

Как понять, что получилось

В каждой строке исходный порядок сохраняется: «дом» — 6 байт, «лес» — 6 байт, «море» — 8 байт. Значения индекса и строки передаются в параметры функции-горутины, программа дожидается всех ответов и не использует time.Sleep как ожидание.

Горутины и замыкания | Go | WebSchool · Go