Назад
Golang

Горутины vs Потоки: В чём разница?

Если вы давно занимаетесь разработкой, то наверняка слышали про горутины. А потоки - это классика из других языков. Давайте разберёмся, чем они отличаются на простых примерах.

1. Что это?

  • Потоки: Это как отдельные вкладки в браузере. Каждый поток - это независимая единица, которая выполняет код. ОС сама решает, когда переключаться между ними. Они используются в языках вроде Java, C++ или Python (с модулем threading).
  • Горутины: Это как потоки, но только реализуемые внутри языка Go. Они созданы, чтобы упростить многопоточность. Горутины запускаются ключевым словом go, и их можно запускать в огромных количествах.

2. Сравнение потребления ресурсов

  • Потоки: Каждый поток требует много памяти (обычно 1-8 МБ на стек) и времени на запуск. Если запустить 1000 потоков, система может замедлиться или даже упасть - ОС не любит, когда их слишком много.
  • Горутины: Каждая горутина берёт всего 2-4 КБ памяти. Вы можете запустить тысячи или даже миллионы - Go-runtime (внутренняя система Go) справится с этим быстро.

3. Как это работает?

  • Потоки: Каждый поток работает отдельно от других. ОС планирует их на процессорные ядра. Переключение между потоками - это как остановить грузовик и запустить другой: медленно и энергозатратно.
  • Горутины: Они "мультиплексируются" на меньшее количество реальных потоков. Go-рантайм сам распределяет горутины по потокам (обычно по одному на ядро процессора). Переключение - лёгкое: быстро и без лишних усилий. Если горутина ждёт (например, сетевой запрос), рантайм сразу даёт работу другой.

Пример кода с горутинами

Чтобы наглядно увидеть, вот простой пример на Go. Мы запустим две горутины: одна печатает "Hello", вторая - "World". Они будут работать параллельно.

package main

import (
    "fmt"
    "time"
)

func sayHello() {
    for i := 0; i  < 5; i++ {
        fmt.Println("Hello")
        time.Sleep(100 * time.Millisecond) // Задержка, чтобы увидеть параллельность
    }
}

func sayWorld() {
    for i := 0; i < 5; i++ {
        fmt.Println("World")
        time.Sleep(100 * time.Millisecond) // Вторая задержка, чтобы увидеть параллельность
    }
}

func main() {
    go sayHello()  // Запускаем горутину для sayHello
    go sayWorld()  // Запускаем горутину для sayWorld

    // Ждём, чтобы горутины завершились (в реальном коде используйте sync.WaitGroup)
    time.Sleep(1 * time.Second)
    fmt.Println("Done!")
}

Что здесь происходит?

  • В main мы запускаем две горутины с помощью go. Они стартуют параллельно основному потоку.
  • Каждая печатает слово 5 раз с задержкой.
  • Без time.Sleep в main программа могла бы завершиться раньше. При написании реальной программы используй - sync.WaitGroup.

Если запустишь, увидишь перемешанный вывод: Hello, World, Hello... Это и есть параллельность!

Подведем итоги, горутины лишь эмулируют потоки, но не являются ими технически(в рамках потоков ОС)

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