В чем разница между процессом и потоком в контексте управления памятью?

Ответ

Основное различие заключается в изоляции памяти.


  • Процесс — это программа в состоянии выполнения. Операционная система выделяет каждому процессу изолированное виртуальное адресное пространство. Процессы не могут напрямую обращаться к памяти друг друга. Для обмена данными они используют механизмы межпроцессного взаимодействия (IPC), которые работают медленнее, чем прямой доступ к памяти.



  • Поток (Thread) — это единица выполнения внутри процесса. Все потоки одного процесса работают в общем адресном пространстве. Это означает, что они разделяют общие данные (глобальные переменные, кучу), что позволяет им обмениваться информацией очень быстро. Однако это также создает риск состояния гонки (race condition), когда несколько потоков одновременно обращаются к общим данным. Для безопасной работы требуется синхронизация (мьютексы, атомарные операции).


Горутины в Go

Горутины — это легковесные потоки, управляемые средой выполнения Go, а не напрямую операционной системой. Они работают в том же адресном пространстве, что и основной процесс Go, и сталкиваются с теми же проблемами синхронизации, что и обычные потоки.

Пример проблемы (гонка данных):

package main

import (
    "fmt"
    "sync"
)

func main() {
    var counter int
    var wg sync.WaitGroup

    wg.Add(2)
    go func() {
        defer wg.Done()
        for i := 0; i < 1000; i++ {
            counter++ // Опасно: одновременная запись
        }
    }()

    go func() {
        defer wg.Done()
        for i := 0; i < 1000; i++ {
            counter++ // Опасно: одновременная запись
        }
    }()

    wg.Wait() // Ждем завершения обеих горутин
    fmt.Println("Результат без синхронизации:", counter) // Результат непредсказуем (не всегда 2000)
}

Решение с использованием мьютекса:

package main

import (
    "fmt"
    "sync"
)

func main() {
    var counter int
    var wg sync.WaitGroup
    var mu sync.Mutex // Мьютекс для защиты counter

    wg.Add(2)
    go func() {
        defer wg.Done()
        for i := 0; i < 1000; i++ {
            mu.Lock()
            counter++
            mu.Unlock()
        }
    }()

    go func() {
        defer wg.Done()
        for i := 0; i < 1000; i++ {
            mu.Lock()
            counter++
            mu.Unlock()
        }
    }()

    wg.Wait()
    fmt.Println("Результат с мьютексом:", counter) // Всегда 2000
}