← Назад ко всем вопросам

Как устроено планирование горутин

1️⃣ Как кратко ответить

Планирование горутин в Go основано на модели M:N, где M — это количество операционных системных потоков, а N — количество горутин. Планировщик Go распределяет горутины по потокам, используя концепцию "P" (процессор), чтобы эффективно использовать доступные системные ресурсы. Это позволяет горутинам выполняться параллельно на нескольких ядрах процессора, обеспечивая высокую производительность и масштабируемость.

2️⃣ Подробное объяснение темы

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

Основные концепции

  1. Горутины: Легковесные потоки, которые управляются планировщиком Go. Они создаются с помощью ключевого слова go и могут выполняться параллельно.

  2. M:N модель: В этой модели M — это количество операционных системных потоков, а N — количество горутин. Планировщик Go распределяет N горутин по M потокам, что позволяет эффективно использовать ресурсы процессора.

  3. P (процессор): Логическая сущность, которая управляет выполнением горутин. Каждый "P" связан с одним системным потоком и может выполнять горутины. Количество "P" по умолчанию равно количеству логических процессоров на машине, но может быть изменено с помощью функции runtime.GOMAXPROCS.

Как это работает

Планировщик Go использует несколько ключевых компонентов для управления выполнением горутин:

  • Горутины: Когда вы создаете новую горутину, она помещается в очередь ожидания выполнения. Горутины могут быть заблокированы, ожидая выполнения операций ввода-вывода или других событий.

  • P (процессоры): Каждый "P" имеет свою очередь горутин. Планировщик распределяет горутины из этой очереди на выполнение в связанном с "P" системном потоке.

  • M (потоки): Системные потоки, которые выполняют горутины. Каждый поток связан с одним "P" и выполняет горутины из его очереди.

Пример кода

package main
​
import (
	"fmt"
	"runtime"
	"sync"
)
​
func main() {
	// Устанавливаем количество процессоров, которые могут одновременно выполнять горутины
	runtime.GOMAXPROCS(2)
​
	var wg sync.WaitGroup
	wg.Add(2)
​
	// Запускаем первую горутину
	go func() {
		defer wg.Done()
		for i := 0; i < 5; i++ {
			fmt.Println("Goroutine 1 - Iteration", i)
		}
	}()
​
	// Запускаем вторую горутину
	go func() {
		defer wg.Done()
		for i := 0; i < 5; i++ {
			fmt.Println("Goroutine 2 - Iteration", i)
		}
	}()
​
	// Ожидаем завершения всех горутин
	wg.Wait()
}

Объяснение кода

  • runtime.GOMAXPROCS(2): Устанавливает количество "P" (процессоров), которые могут одновременно выполнять горутины. В данном случае, это значение равно 2, что позволяет двум горутинам выполняться параллельно.

  • sync.WaitGroup: Используется для ожидания завершения всех горутин. Мы добавляем две горутины в группу ожидания с помощью wg.Add(2).

  • go func() { ... }(): Создает и запускает новую горутину. Внутри каждой горутины выполняется цикл, который выводит номер итерации.

  • wg.Done(): Уменьшает счетчик группы ожидания на 1, сигнализируя о завершении горутины.

  • wg.Wait(): Блокирует выполнение основной программы до тех пор, пока счетчик группы ожидания не станет равным нулю, то есть пока все горутины не завершат выполнение.

Зачем это нужно

Планирование горутин позволяет эффективно использовать многопоточность и параллелизм, что особенно важно для высоконагруженных приложений. Это позволяет разработчикам писать конкурентные программы, которые могут обрабатывать множество задач одновременно, не беспокоясь о сложностях управления потоками на уровне операционной системы.

Тема: Конкурентность
Стадия: Tech

🔒 Подпишись на бусти автора и стань Алигатором, чтобы получить полный доступ к функционалу сайта и отслеживать свой прогресс!

Твои заметки