Ответ
Это три разных уровня абстракции для выполнения кода.
Процесс (Process)
- Что это: Экземпляр запущенной программы. Операционная система выделяет ему изолированное адресное пространство памяти, файловые дескрипторы и другие ресурсы.
- Изоляция: Процессы полностью изолированы друг от друга. Один процесс не может напрямую получить доступ к памяти другого.
- Взаимодействие: Через механизмы межпроцессного взаимодействия (IPC), такие как пайпы, сокеты, общая память.
- Стоимость: Создание и переключение контекста между процессами — очень дорогостоящая операция для ОС.
Поток (OS Thread)
- Что это: Наименьшая единица выполнения, которой управляет планировщик операционной системы. Потоки выполняются в рамках одного процесса.
- Изоляция: Потоки одного процесса разделяют общее адресное пространство памяти, но у каждого есть свой стек вызовов.
- Взаимодействие: Очень простое и быстрое, так как они могут напрямую читать и писать в общую память (требует синхронизации: мьютексы, семафоры).
- Стоимость: Создание и переключение контекста дешевле, чем у процессов, но все еще имеет значительные накладные расходы.
Горутина (Goroutine)
- Что это: Легковесный "поток", управляемый средой выполнения Go (Go runtime), а не операционной системой.
- Изоляция: Горутины выполняются в общем адресном пространстве, как и потоки.
- Взаимодействие: Идиоматичный способ — через каналы (
chan
), что позволяет избежать проблем с состоянием гонки. Прямой доступ к общей памяти также возможен, но требует примитивов синхронизации (sync
пакет). - Стоимость: Создание и переключение контекста чрезвычайно дешевы. Можно легко запустить сотни тысяч и даже миллионы горутин.
Ключевое отличие горутин:
Go использует модель планирования M:N, где M горутин выполняются на N потоках ОС (где M обычно гораздо больше N). Go runtime сам решает, какая горутина на каком потоке будет выполняться, эффективно мультиплексируя их. Это позволяет достичь высокой степени параллелизма при меньшем потреблении ресурсов по сравнению с традиционной моделью "один поток на задачу".
Пример с горутиной (идиоматичное ожидание завершения):
import (
"fmt"
"sync"
"time"
)
func main() {
// WaitGroup используется для ожидания завершения всех горутин
var wg sync.WaitGroup
// Сообщаем WaitGroup, что мы ожидаем завершения одной горутины
wg.Add(1)
go func() {
// defer гарантирует, что Done() будет вызван при выходе из функции
defer wg.Done()
fmt.Println("Hello from goroutine")
time.Sleep(50 * time.Millisecond)
}()
fmt.Println("Hello from main")
// Блокируем выполнение main, пока счетчик WaitGroup не станет равен нулю
wg.Wait()
fmt.Println("Goroutine finished, exiting")
}