Ответ
Горутина — это легковесная, управляемая средой выполнения (runtime) Go, функция или метод, выполняющийся конкурентно с другими горутинами. Это ключевая концепция для достижения конкурентности в Go.
Ключевые отличия от системных потоков:
-
Стоимость создания и память:
- Горутины: Очень дешевы. Начальный размер стека всего ~2 КБ, который может динамически расти и сжиматься. Можно легко запустить сотни тысяч и даже миллионы горутин.
- Потоки ОС: Дорогие. Имеют фиксированный и значительно больший размер стека (обычно 1-8 МБ). Создание большого количества потоков сильно нагружает ОС.
-
Управление и переключение контекста:
- Горутины: Управляются планировщиком Go в пространстве пользователя (user space). Переключение между горутинами происходит очень быстро и не требует дорогостоящих системных вызовов.
- Потоки ОС: Управляются ядром операционной системы. Переключение контекста между потоками — медленная операция.
-
Модель выполнения (M:N):
- Планировщик Go распределяет M горутин для выполнения на N системных потоках (где M обычно гораздо больше N). Это позволяет эффективно утилизировать процессорные ядра.
Идиоматичный пример с sync.WaitGroup:
Использование time.Sleep для синхронизации — плохая практика. Правильнее использовать примитивы синхронизации, например sync.WaitGroup.
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var wg sync.WaitGroup
wg.Add(1) // Сообщаем, что нужно дождаться одну горутину
go func() {
defer wg.Done() // Сигнализируем о завершении работы в конце функции
fmt.Println("Горутина работает!")
time.Sleep(time.Second)
}()
fmt.Println("Основная функция ждет завершения горутины...")
wg.Wait() // Блокируем выполнение, пока счетчик WaitGroup не станет равен 0
fmt.Println("Горутина завершила работу.")
} Ответ 18+ 🔞
А, ну ты про эти штуки, горутины, да? Ну слушай, сейчас объясню так, что даже твоя бабка поймёт, хотя она, конечно, нихуя в этом не смыслит.
Представь себе, блядь, обычный поток в операционке — это как здоровенный КамАЗ. Места дохуя занимает, бензина жрёт, развернуться ебись-свинья. Запустить тысячу таких — и твой комп накрывается медным тазом, потому что операционка с ума сходит, пытаясь их всех переключать.
А горутина — это, сука, как электросамокат. Лёгкий, ёбушки-воробушки, места в памяти — кот наплакал, пару килобайт. Их можно, блядь, как тараканов развести — сотни тысяч! И управляет ими не ядро ОС, а свой, родной планировщик Go, который как хитрая жопа, распределяет эту ораву самокатчиков по настоящим потокам-КамАЗам. Перепрыгнул с одного на другой — и даже не чихнул. Быстро, дёшево, сердито.
А теперь смотри, как правильно делать, а то некоторые мудаки через time.Sleep синхронизируются — это пиздец какой-то, волнение ебать. Нормальные люди используют WaitGroup. Это как счётчик, понимаешь? Сказал ему: «Слушай, я тут одну горутину запустил, жди её». А в конце горутина пишет: «Всё, я сделала, свободна!». И основная программа сидит и ждёт, пока этот счётчик не обнулится. Красота, а не жизнь.
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var wg sync.WaitGroup
wg.Add(1) // Говорим: "Эй, мудила, запомни — одну ждём!"
go func() {
defer wg.Done() // А это волшебное слово, которое скажет "Всё, я кончил!" даже если тут пиздец случится
fmt.Println("Горутина работает!")
time.Sleep(time.Second)
}()
fmt.Println("Основная функция ждет завершения горутины...")
wg.Wait() // Тут мы встаём как вкопанные и не двигаемся дальше, пока счётчик не ноль
fmt.Println("Горутина завершила работу. Ура, товарищи!")
}
Вот и вся магия, ёпта. Никаких КамАЗов, одни быстрые самокаты, и все довольны. Главное — синхронизацию не проеби, а то получишь race condition, и тогда пидары налетят.