← Все этапы
05 · КОНКУРЕНТНОСТЬУрок 29 из 3320 минут

Конкурентность и предсказуемый результат

Запускаем независимые вычисления одновременно и собираем результат в нужном порядке.

Несколько задач в работе одновременно

В обычном последовательном коде следующая операция начинается после предыдущей. При конкурентном выполнении несколько задач продвигаются независимо: планировщик Go распределяет горутины по доступным потокам и процессорам. Независимые вычисления могут выполняться параллельно, а ожидание внешней операции может чередоваться с работой других задач.

Конкурентность не задаёт порядок, в котором задачи завершатся. Поэтому программа не должна полагаться на то, какая горутина первой напечатает строку или изменит общее значение. Если результат должен быть воспроизводимым, собери ответы, а затем выведи их в определённом порядке.

Пример

package main

import (
	"fmt"
	"sort"
	"sync"
)

type taskResult struct {
	position int
	value    int
}

func main() {
	numbers := []int{8, 3, 6, 2}
	results := make(chan taskResult, len(numbers))
	var workers sync.WaitGroup

	for position, number := range numbers {
		workers.Add(1)
		go func(position, number int) {
			defer workers.Done()
			results <- taskResult{position: position, value: number * number}
		}(position, number)
	}

	go func() {
		workers.Wait()
		close(results)
	}()

	completed := make([]taskResult, 0, len(numbers))
	for result := range results {
		completed = append(completed, result)
	}
	sort.Slice(completed, func(i, j int) bool {
		return completed[i].position < completed[j].position
	})

	for _, result := range completed {
		fmt.Printf("позиция %d: %d\n", result.position, result.value)
	}
}

Каждый расчёт независим, поэтому его можно начать отдельной горутиной. Буфер результатов вмещает по одному ответу на задачу: горутина может отправить ответ, пока основная функция собирает остальные. Группа ожидания сообщает координатору, когда все отправители завершили работу; только координатор закрывает канал после ожидания.

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

Практика

Попробуйте сами

Дан список чисел [5, 1, 4, 2]. Запусти расчёт куба каждого числа в отдельной горутине. Передай номер позиции вместе с ответом, дождись завершения всех расчётов и выведи значения в исходном порядке.

Проверка результата

Как понять, что получилось

Вывод всегда содержит четыре строки в позициях 0–3 со значениями 125, 1, 64 и 8 именно в этом порядке. Отправка результатов не выполняется в одну общую переменную без синхронизации; все горутины завершены до конца программы.

Конкурентность и предсказуемый результат | Go | WebSchool · Go