Какая модель конкурентности используется в Go и в чем ее особенность?

Ответ

В Go используется модель конкурентности, основанная на горутинах (goroutines) и каналах (channels). Эта модель следует философии: "Не обменивайтесь данными через общую память; вместо этого обменивайтесь данными через каналы" ("Do not communicate by sharing memory; instead, share memory by communicating.").

Ключевые компоненты и особенности:

  1. Горутины (Goroutines):

    • Это легковесные потоки, управляемые средой выполнения Go (Go runtime), а не ядром операционной системы.
    • Они имеют небольшой начальный стек (~2KB), который может динамически расти, что позволяет создавать сотни тысяч и даже миллионы горутин одновременно.
    • Запуск горутины очень прост — достаточно использовать ключевое слово go перед вызовом функции: go myFunction().
  2. Планировщик Go (M:N Scheduler):

    • Go использует планировщик, который отображает M горутин на N потоков операционной системы (где M обычно гораздо больше N). Это позволяет эффективно утилизировать процессорные ядра, даже если многие горутины заблокированы на операциях ввода-вывода.
  3. Каналы (Channels):

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

Пример (Worker Pool):

Этот пример демонстрирует, как несколько горутин-воркеров обрабатывают задачи, поступающие из одного канала, и отправляют результаты в другой.

package main

import (
    "fmt"
    "time"
)

// worker получает задачи из канала jobs и отправляет результат в results
func worker(id int, jobs <-chan int, results chan<- int) {
    for j := range jobs {
        fmt.Printf("Worker %d started job %dn", id, j)
        time.Sleep(time.Second) // Имитация работы
        results <- j * 2
    }
}

func main() {
    const numJobs = 5
    jobs := make(chan int, numJobs)
    results := make(chan int, numJobs)

    // Запуск 3 горутин-воркеров
    for w := 1; w <= 3; w++ {
        go worker(w, jobs, results)
    }

    // Отправка 5 задач в канал jobs
    for j := 1; j <= numJobs; j++ {
        jobs <- j
    }
    close(jobs) // Закрываем канал, чтобы воркеры завершились после обработки всех задач

    // Получение результатов
    for r := 1; r <= numJobs; r++ {
        fmt.Println("Result:", <-results)
    }
}

Эта модель позволяет писать простой, понятный и высокопроизводительный конкурентный код.