Ответ
В Go многозадачность (или, точнее, конкурентность) реализуется через горутины (goroutines) — легковесные потоки, управляемые рантаймом Go. Они работают поверх системных потоков, но потребляют значительно меньше ресурсов (стартовый стек ~2KB, динамически расширяемый).
Горутины запускаются с помощью ключевого слова go
:
go func() {
fmt.Println("Работаю в горутине")
}()
Планировщик Go (scheduler) эффективно распределяет горутины по доступным CPU-ядрам, используя модель M:N (M горутин на N системных потоков). Для безопасной синхронизации и обмена данными между горутинами используются каналы (channels) и примитивы из пакета sync
(Mutex, RWMutex, WaitGroup, Cond и др.).
Пример использования sync.WaitGroup
для ожидания завершения горутин:
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var wg sync.WaitGroup
wg.Add(1) // Увеличиваем счетчик на 1
go func() {
defer wg.Done() // Уменьшаем счетчик по завершении горутины
fmt.Println("Горутина 1 начала работу")
time.Sleep(time.Second) // Имитация работы
fmt.Println("Горутина 1 завершила работу")
}()
wg.Add(1)
go func() {
defer wg.Done()
fmt.Println("Горутина 2 начала работу")
time.Sleep(500 * time.Millisecond)
fmt.Println("Горутина 2 завершила работу")
}()
fmt.Println("Ожидаем завершения всех горутин...")
wg.Wait() // Блокируемся до тех пор, пока счетчик не станет равен 0
fmt.Println("Все горутины завершили работу.")
}
Главные преимущества такого подхода: простота использования, автоматическое масштабирование конкурентных задач и эффективное использование ресурсов системы, что делает Go отличным выбором для высоконагруженных сетевых сервисов.