Запуск горутин
Ключевое слово go, жизненный цикл горутины и WaitGroups
«Запуск горутин» — бесплатный урок Go Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Go Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Go Academy содержит 4 уроков всего.
Что такое goroutine
goroutine — это лёгкая функция, выполняющаяся параллельно и управляемая средой выполнения Go. goroutine гораздо дешевле потоков ОС — можно запускать тысячи таких функций:
- Запускаются с помощью ключевого слова
go - Начальный стек — около 2 КБ (увеличивается динамически)
- Планируются средой выполнения Go, а не ОС
- Обмениваются данными через каналы, а не через общую память
Ключевое слово go
Добавьте go перед любым вызовом функции, чтобы запустить её как goroutine:
package main
import ("fmt"; "time")
func greet(name string) {
fmt.Printf("Hello, %s!\n", name)
}
func main() {
go greet("Alice") // runs concurrently
go greet("Bob") // runs concurrently
time.Sleep(100 * time.Millisecond) // wait for goroutines
fmt.Println("done")
}Жизненный цикл goroutine
goroutine запускается при выполнении оператора go и завершается, когда её функция возвращает управление. Если функция main возвращает управление, все goroutine завершаются:
package main
import ("fmt"; "time")
func worker(id int) {
fmt.Printf("Worker %d starting\n", id)
time.Sleep(time.Duration(id) * 50 * time.Millisecond)
fmt.Printf("Worker %d done\n", id)
}
func main() {
for i := 1; i <= 3; i++ {
go worker(i)
}
time.Sleep(300 * time.Millisecond) // naive wait
}sync.WaitGroup для координации
Используйте sync.WaitGroup вместо сна, чтобы дождаться завершения goroutine:
package main
import ("fmt"; "sync")
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done() // signal completion
fmt.Printf("Worker %d working\n", id)
}
func main() {
var wg sync.WaitGroup
for i := 1; i <= 5; i++ {
wg.Add(1) // register one goroutine
go worker(i, &wg)
}
wg.Wait() // block until all Done()
fmt.Println("all workers finished")
}Анонимные goroutine
Запускайте анонимную функцию непосредственно как goroutine — это удобно для небольших параллельных задач:
package main
import ("fmt"; "sync")
func main() {
var wg sync.WaitGroup
for i := 0; i < 3; i++ {
i := i // capture loop variable
wg.Add(1)
go func() {
defer wg.Done()
fmt.Printf("goroutine %d\n", i)
}()
}
wg.Wait()
}Особенность захвата переменной цикла
Если не захватить переменную цикла, все goroutine могут увидеть одно и то же (последнее) значение — это классическая ошибка параллелизма в Go:
package main
import ("fmt"; "sync")
func main() {
var wg sync.WaitGroup
for i := 0; i < 3; i++ {
wg.Add(1)
go func(n int) { // pass i as argument — safe
defer wg.Done()
fmt.Println(n) // prints 0, 1, 2 in some order
}(i)
}
wg.Wait()
}goroutine и потоки
Почему goroutine предпочтительнее потоков ОС:
- Начальный стек 2 КБ против примерно 1 МБ у потоков ОС
- Создание занимает микросекунды, а не миллисекунды
- Среда выполнения Go распределяет goroutine между потоками ОС в количестве GOMAXPROCS
- Переключение между goroutine выполняется кооперативно — без участия ядра
- Go берёт планирование на себя, а Вы сосредотачиваетесь на логике
GOMAXPROCS
runtime.GOMAXPROCS управляет количеством потоков ОС, которые одновременно выполняют goroutine (по умолчанию используется количество процессоров):
package main
import ("fmt"; "runtime")
func main() {
// Default: number of available CPU cores
fmt.Println("CPUs:", runtime.NumCPU())
fmt.Println("GOMAXPROCS:", runtime.GOMAXPROCS(0)) // 0 = query without changing
// Set to 1 for debugging race conditions:
// runtime.GOMAXPROCS(1)
fmt.Println("Goroutines:", runtime.NumGoroutine())
}Утечки goroutine
Утечка goroutine возникает, когда goroutine запущена, но никогда не завершается. Распространённые причины: заблокированное получение из канала, заблокированная блокировка или отсутствие отмены:
package main
import ("fmt"; "time"; "runtime")
func leaky() {
ch := make(chan int) // nobody sends to ch
go func() {
v := <-ch // blocks forever — goroutine leaked!
fmt.Println(v)
}()
}
func main() {
leaky()
time.Sleep(10 * time.Millisecond)
fmt.Println("goroutines:", runtime.NumGoroutine()) // > 1
}Рекомендации по goroutine
Рекомендации по работе с goroutine:
- Всегда знайте, каким образом goroutine завершится
- Для синхронизации используйте WaitGroup или каналы, а не сон
- Передавайте переменные цикла как аргументы функции, а не через замыкание
- Отменяйте goroutine с помощью контекста, не оставляйте их выполняться
- Используйте детектор состязаний:
go run -race main.go
Краткая проверка
Как правильно избежать ошибки захвата переменной цикла в goroutine?
Повторение: запуск goroutine
Краткое содержание:
- Запускайте с помощью
go fn()— это легко и дёшево - Используйте
sync.WaitGroupдля ожидания:Add(1),defer Done(),Wait() - Захватывайте переменные цикла через аргумент или повторное объявление
- Всегда знайте, как завершается каждая goroutine, — предотвращайте утечки
- Используйте флаг
-raceдля обнаружения состязаний данных
Изучай Go с ИИ-репетитором — бесплатно
Пиши и запускай код прямо в браузере, получай мгновенную помощь от ИИ-репетитора 24/7 и продолжи учиться на сайте или в приложении.
- Курсы
- 51
- Уроки
- 203
Часто задаваемые вопросы
Урок «Запуск горутин» бесплатный?
Да — полный текст урока «Запуск горутин» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Go Academy, подпишись на CoddyKit PRO. Курс Go Academy содержит 4 уроков всего.
Чему я научусь в уроке «Запуск горутин»?
Ключевое слово go, жизненный цикл горутины и WaitGroups Ты практикуешь Go Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Go Academy?
Предыдущий опыт не требуется. Go Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.
Сколько времени занимает урок «Запуск горутин»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Go Academy?
Да. Каждый урок Go Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.