Ответ
В Go горутины (легковесные потоки) взаимодействуют и синхронизируются с помощью нескольких ключевых механизмов:
-
Запуск горутины — создание и запуск новой горутины:
go func() { // Код, выполняемый в новой горутине fmt.Println("Привет из горутины!") }()
-
Ожидание завершения —
sync.WaitGroup
используется для ожидания завершения одной или нескольких горутин:var wg sync.WaitGroup wg.Add(1) // Увеличиваем счетчик на 1 go func() { defer wg.Done() // Уменьшаем счетчик по завершении горутины fmt.Println("Горутина выполняет работу...") time.Sleep(time.Second) }() wg.Wait() // Блокирует выполнение, пока счетчик не станет 0 fmt.Println("Все горутины завершены.")
-
Синхронизация и передача данных через каналы — каналы (
chan
) являются основным способом безопасной передачи данных и синхронизации между горутинами (принцип "Do not communicate by sharing memory; instead, share memory by communicating."):ch := make(chan int) // Создаем канал для int go func() { ch <- 42 // Отправляем значение в канал }() value := <-ch // Получаем значение из канала (блокирующая операция) fmt.Printf("Получено: %dn", value)
-
Блокировка/разблокировка доступа к общим данным —
sync.Mutex
(мьютекс) илиsync.RWMutex
(мьютекс чтения/записи) используются для защиты общих ресурсов от одновременного доступа нескольких горутин:var mu sync.Mutex var counter int go func() { mu.Lock() // Захватываем мьютекс counter++ // Безопасно изменяем общую переменную mu.Unlock() // Освобождаем мьютекс }()
-
Остановка по контексту —
context.Context
используется для передачи дедлайнов, отмены сигналов и значений между API-границами и горутинами:ctx, cancel := context.WithCancel(context.Background()) go func(ctx context.Context) { select { case <-ctx.Done(): // Ожидаем сигнала отмены fmt.Println("Горутина отменена.") return case <-time.After(5 * time.Second): fmt.Println("Горутина завершила работу по таймауту.") } }(ctx) time.Sleep(time.Second) // Даем горутине немного поработать cancel() // Отправляем сигнал отмены time.Sleep(time.Second) // Ждем завершения горутины
-
Обработка паники —
recover()
используется внутриdefer
для перехвата паники в горутине, предотвращая крах всей программы:go func() { defer func() { if r := recover(); r != nil { fmt.Printf("Горутина паниковала: %vn", r) } }() panic("Что-то пошло не так!") }() time.Sleep(time.Second) // Даем время для паники и восстановления
Эти механизмы позволяют эффективно управлять параллелизмом и обеспечивать безопасность данных в Go-приложениях.