Функция должна генерировать числа и записывать их в канал ch
. Для каждого числа должна быть вызвана функция fn()
. Используйте такой алгоритм генерации:
- N(0) = 1;
- N(i) = N(i-1) + 1
Generator()
прекращает работу, когда поступил сигнал об отмене контекста ctx
.
Подсказка 1
Используйте бесконечный цикл for
и конструкцию select
с проверкой на <-ctx.Done()
. Перед выходом из функции закройте канал ch.
Эта функция обрабатывает числа. Она должна читать числа из канала in
до тех пор, пока он не закроется. Полученное число нужно записать в канал out
и сделать паузу на 1 миллисекунду. Если канал in
закрылся, то нужно закрыть канал out
и завершить работу функции.
Подсказка 2
Можно использовать бесконечный цикл и оператор v, ok := <-in
, который позволяет отследить закрытие канала.
Здесь необходимо создать контекст ctx
, который должен самостоятельно отмениться через одну секунду.
Подсказка 3
Используйте context.WithTimeout()
и не оставляйте cancel()
без присмотра.
В этом фрагменте нужно пройтись по всем каналам из слайса outs
и для каждого из них запустить горутину с анонимной функцией. Горутина должна читать данные из переданного ей канала, до тех пор, пока он не закроется. Чтобы дождаться завершения всех этих горутин, нужно воспользоваться механизмом WaitGroup
. Горутина должна подсчитывать обработанные числа (увеличивать соответствующий счётчик в слайсе amounts
) передавать значения в канал chOut
.
Подсказка 4
Перед вызовом горутины не забудьте вызвать wg.Add(1)
, а при её завершении — wg.Done()
. Используйте анонимную функцию с двумя параметрами go func(in <-chan int64, i int64){}
, где in
— очередной канал из outs
, а i
— его индекс. Не забывайте про увеличение счётчика amounts[i]++
.
Осталось прочитать все данные из канала chOut
. При этом нужно подсчитать в переменной count
общее количество чисел, а в переменной sum
— сумму всех чисел.
Подсказка 5
Воспользуйтесь конструкцией for ... range
, которая будет читать числа из канала до его закрытия.
Вроде ваше программа работает и выдаёт ожидаемые результаты; но в коде есть кусок, который может привести к состоянию гонки, если программа изменится. Попробуйте найти проблемный фрагмент самостоятельно, прежде чем читать дальше.
Читать дальше
Состояние гонки в основном возникает, когда несколько горутин одновременно изменяют общие данные. Посмотрите внимательно на код программы. Предположим, кто-то решит запускать несколько горутин для генерации чисел. Это может привести к состоянию гонки: анонимная функция, которая передаётся в Generator()
, изменяет одни и те же переменные — inputSum
и inputCount
. Сделайте увеличение этих переменных потокобезопасным. Вы можете использовать мьютекс или функцию atomic.AddInt64()
.
После добавления необходимого кода и успешной отработки, программа должна вывести в консоль примерно такие строки:
Количество чисел 4558 4558
Сумма чисел 10389961 10389961
Разбивка по каналам [913 912 913 910 910]
У вас значения будут другими, но числа в первой и второй строчках, которые указаны через пробел, должны совпадать. Числа слайса amount
должны быть практически одинаковыми. Они показывают, сколько значений передалось через соответствующий канал outs[i]
. Если через один канал прошло, например, 800 чисел, а через другой — 900, это сигнал о неполадках.
Вы можете изменить значение константы NumOut
и сравнить результаты при NumOut
, равной 2, 5, 10, 15. Видно, что чем больше используется горутин, тем больше чисел обрабатывается в сумме, но при этом каждая горутина обрабатывает меньше чисел. Вот пример вывода при NumOut
, равной 15:
Количество чисел 13087 13087
Сумма чисел 85641328 85641328
Разбивка по каналам [870 873 873 873 873 873 872 872 874 872 871 872 874 871 874]
Если программа выдаёт ожидаемые результаты, можно отправлять её на ревью. Надеемся, что итоговое задание напомнило вам основные конструкции и инструменты работы с многопоточностью, и вы закрепили полученные знания на практике.