Как горутины взаимодействуют с памятью? Разделяют ли они общее адресное пространство?

Ответ

Да, все горутины, запущенные в рамках одного процесса, разделяют одно и то же адресное пространство. Горутины — это легковесные потоки, управляемые средой выполнения Go (Go runtime), а не напрямую операционной системой. Это позволяет запускать тысячи и даже миллионы горутин одновременно.

Поскольку память общая, при одновременном доступе нескольких горутин к одним и тем же данным могут возникнуть состояния гонки (data races). Для предотвращения этого необходимы механизмы синхронизации.

В Go есть два основных подхода к безопасной работе с общей памятью:

  1. Мьютексы (sync.Mutex): Используются для защиты критических секций кода, гарантируя, что только одна горутина в один момент времени может выполнять код, изменяющий общие данные.

    package main
    
    import (
        "fmt"
        "sync"
        "time"
    )
    
    var counter int
    var wg sync.WaitGroup
    var mu sync.Mutex
    
    func increment() {
        defer wg.Done()
        mu.Lock() // Блокируем доступ для других горутин
        counter++
        mu.Unlock() // Освобождаем блокировку
    }
    
    func main() {
        for i := 0; i < 1000; i++ {
            wg.Add(1)
            go increment()
        }
        wg.Wait() // Ждем завершения всех горутин
        fmt.Println("Итоговый счетчик:", counter) // Итоговый счетчик: 1000
    }
  2. Каналы (channels): Реализуют идеологию Go: "Не общайтесь, разделяя память; вместо этого разделяйте память, общаясь". Данные передаются между горутинами через каналы, что исключает необходимость в явных блокировках.

Итог: Горутины разделяют память, что делает их эффективными, но требует от разработчика внимательного отношения к синхронизации доступа к данным с помощью мьютексов или каналов.