## Домашнее задание №5 «Параллельное исполнение» Необходимо написать функцию для параллельного выполнения заданий в n параллельных горутинах: * количество создаваемых горутин не должно зависеть от числа заданий, т.е. функция должна запускать n горутин для конкурентной обработки заданий и, возможно, еще несколько вспомогательных горутин; * функция должна останавливать свою работу, если произошло m ошибок; * после завершения работы функции (успешного или из-за превышения m) не должно оставаться работающих горутин. Нужно учесть, что задания могут выполняться разное время, а длина списка задач `len(tasks)` может быть больше или меньше n. Значение m <= 0 трактуется на усмотрение программиста: - или это знак игнорировать ошибки в принципе; - или считать это как "максимум 0 ошибок", значит функция всегда будет возвращать `ErrErrorsLimitExceeded`; - на эту логику следует написать юнит-тест. Граничные случаи: * если задачи работают без ошибок, то выполнятся `len(tasks)` задач, т.е. все задачи; * если в первых выполненных m задачах (или вообще всех) происходят ошибки, то всего выполнится не более n+m задач. **(*) Дополнительное задание: написать тест на concurrency без time.Sleep** Придумайте тест, который проверит concurrency другим способом. Текущий тест "tasks without errors" использует time.Sleep и подсчет времени выполнения, чтобы сделать вывод о конкурентности использования. Проблема тестов на слипчиках в том, что на CI часто не хватает CPU и подобные тесты работают нестабильно. Подсказка: используйте `require.Eventually`. --- #### Пример Имеем 10 задач, n=4 воркера, m=2 ошибки. - Запускаем: ``` --------------ok (узнал, что лимит превышен и остановился) -----------err -------err --------------------ok ``` Выполнится 4 задачи (2 успешно) <= 4 + 2, остальные задачи  не берем. - Другая ситуация, работающие воркеры успели еще взять задач: ``` ------ok--------ok (узнал, что лимит превышен и остановился) -----------err ---err --------ok-------ok ``` Выполнится 6 задач (4 успешно) <= 4 + 2, остальные задачи не берем. - Ошибок не было: ``` -------ok-----ok-----ok-----ok (1 воркер выполнил 4 задачи) -----------ok-------------ok (2 воркер выполнил 2 задачи) -----ok---------ok---------ok (3 воркер выполнил 3 задачи) --------------------ok (4 воркер выполнил 1 задачу) ``` Выполнится 10 задач (10 успешно): задач не осталось, воркеры остановились. --- При необходимости можно выделять дополнительные функции / ошибки. ### Критерии оценки - Пайплайн зелёный - 4 балла - Добавлены новые юнит-тесты - до 4 баллов - Понятность и чистота кода - до 2 баллов #### Зачёт от 7 баллов ### Подсказки - https://en.wikipedia.org/wiki/Producer%E2%80%93consumer_problem - `sync.WaitGroup` - `go test -v -race -count=100 .` ### Частые ошибки 1) `racedetector` ругается на строчку с ассертом в тестах: - простой случай: после выхода из `Run` остаются висячие горутины, отсюда и получаем `data race` - ассерт в тестах неатомарно обращается к `runTasksCount`, в то время как зомби-горутины атомарно пытаюся её поменять. - случай посложнее: один тест завершается успешно, но висячие горутины, им порожденные, аффектят ассерты в последующих тестах. 2) Запускаются лишние горутины (инструкции `go`). Их количество за все время работы `Run` должно быть n (плюс, возможно, еще одна-две, если по другому не получается). В некоторых решениях ошибочно контроллируется количество одновременно работающих, а не общее количество. **Решение**: внимательно посмотреть места выхода из функции и гарантировать, что все порождённые вами горутины завершились к этому моменту.