Страницы

Поиск по вопросам

вторник, 5 марта 2019 г.

Многопоточность и параллельность при запуске внешнего EXE несколько раз

Добрый день, уважаемое сообщество.
Прошу провести code review и дать совет. Есть программа. Ей надо запустить один и тот же внешний EXE файл N раз с разными входными параметрами. Дождаться окончания выполнения всех запущенных процессов и собрать результаты в единый, допустим, массив.
Как использовать параллелизм и асинхронность (если последняя тут нужна) с максимальной эффективностью?
Я пока сделала такое решение:
List> processignDataList = ... //список кортежей с входными данными Task[] stackTasks = new Task[processignDataList.Count]; // массив всех тасков List> tasksResults = new List>(); //список кортежей с результатами отработки тасков int i = 0; // счетчик, нужен только для указания элемента массива
foreach (Tuple pair in processignDataList) { stackTasks[i] = Task.Factory.StartNew(() => tasksResults.Add(Tuple.Create(pair.Item1, pairProcessing(cSettings, pair)))); i++; } Task.WaitAll(stackTasks);
Метод string pairProcessing()
берет свойства из специального объекта cSettings, входные параметры из pair, запускает EXE как Process - process.Start(); там же находится ограничение от бесконечного зависания - process.WaitForExit(3000) потом проверка, что process.Close(); анализ StandardOutput, и на его основе формирование некой строки и ее возврат как результата работы метода pairProcessing
Достигаю ли я с таким решением запуска нескольких экземпляров EXE и параллельного их выполнения? Нужны ли какие-то дополнительные пометки методу pairProcessing(), есть к нему какие-то требования или он может быть любым? Знаете ли вы решения лучше? (а решение лучше есть всегда...) Есть ли косяки? Спасибо.


Ответ

Во-первых, у вас нет синхронизации доступа к tasksResults - тут может быть неприятная гонка.
Во-вторых, если процессов будет много - у вас могут закончиться потоки в пуле, что нежелательно ограничит параллелизм. Впрочем, слишком много процессов все равно не смогут работать параллельно.

Первая проблема решается довольно просто - вместо записи в список внутри задачи надо позволить задаче вернуть значение:
List>> stackTasks = new List>>();
foreach (Tuple pair in processignDataList) { stackTasks.Add(Task.Factory.StartNew(() => Tuple.Create(pair.Item1, pairProcessing(cSettings, pair)))); }
Tuple[] tasksResults = Task.WhenAll(stackTasks).Result;
Также можно воспользоваться linq:
Tuple[] tasksResults = Task.WhenAll( from pair in processignDataList select Tuple.Create(pair.Item1, pairProcessing(cSettings, pair))) ).Result;
или так:
Tuple[] tasksResults = Task.WhenAll( processignDataList.Select(pair => Tuple.Create(pair.Item1, pairProcessing(cSettings, pair)))) ).Result;

Для решения второй проблемы надо переходить от задач к потокам. Или увеличить размер пула потоков при помощи ThreadPool.SetMaxThreads и ThreadPool.SetMinThreads

Комментариев нет:

Отправить комментарий