Ответ
В Go используется модель конкурентности, основанная на горутинах (goroutines) и каналах (channels). Эта модель следует философии: "Не обменивайтесь данными через общую память; вместо этого обменивайтесь данными через каналы" ("Do not communicate by sharing memory; instead, share memory by communicating.").
Ключевые компоненты и особенности:
Горутины (Goroutines):
- Это легковесные потоки, управляемые средой выполнения Go (Go runtime), а не ядром операционной системы.
- Они имеют небольшой начальный стек (~2KB), который может динамически расти, что позволяет создавать сотни тысяч и даже миллионы горутин одновременно.
- Запуск горутины очень прост — достаточно использовать ключевое слово
go
перед вызовом функции:go myFunction()
.
Планировщик Go (M:N Scheduler):
- Go использует планировщик, который отображает M горутин на N потоков операционной системы (где M обычно гораздо больше N). Это позволяет эффективно утилизировать процессорные ядра, даже если многие горутины заблокированы на операциях ввода-вывода.
Каналы (Channels):
- Это типизированные конвейеры, через которые можно отправлять и получать значения, обеспечивая безопасную коммуникацию между горутинами. Использование каналов помогает избежать состояний гонки (race conditions) без необходимости в явных блокировках (
sync.Mutex
).
- Это типизированные конвейеры, через которые можно отправлять и получать значения, обеспечивая безопасную коммуникацию между горутинами. Использование каналов помогает избежать состояний гонки (race conditions) без необходимости в явных блокировках (
Пример (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)
}
}
Эта модель позволяет писать простой, понятный и высокопроизводительный конкурентный код.