Страницы

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

понедельник, 23 декабря 2019 г.

Как выполнить цикл еще раз в Parallel.For?

#c_sharp #tpl


Допустим я в 100 потоков качаю картинки. Если какая либо итерация вызвало исключение
- как его повторить по новой?

Parallel.For(0, newLst.Count, new ParallelOptions { MaxDegreeOfParallelism = 100
}, (i) =>
      {
           DownloadImage(newLst[i]);
      });

    


Ответы

Ответ 1



Сделять DownloadImageAsync, который дергать не в Parallel.For, а просто ограничив число одновременно выполняемых задач, например с помощью такого кода: public static IEnumerable> ForEachAsync( this IEnumerable source, Func> selector, int degreeOfParallelism) { Contract.Requires(source != null); Contract.Requires(selector != null); // We need to know all the items in the source before starting tasks var tasks = source.ToList(); int completedTask = -1; // Creating an array of TaskCompletionSource that would holds // the results for each operations var taskCompletions = new TaskCompletionSource[tasks.Count]; for(int n = 0; n < taskCompletions.Length; n++) taskCompletions[n] = new TaskCompletionSource(); // Partitioner would do all grunt work for us and split // the source into appropriate number of chunks for parallel processing foreach (var partition in Partitioner.Create(tasks).GetPartitions(degreeOfParallelism)) { var p = partition; // Loosing sync context and starting asynchronous // computation for each partition Task.Run(async () => { while (p.MoveNext()) { var task = selector(p.Current); // Don't want to use empty catch . // This trick just swallows an exception await task.ContinueWith(_ => { }); int finishedTaskIndex = Interlocked.Increment(ref completedTask); taskCompletions[finishedTaskIndex].FromTask(task); } }); } return taskCompletions.Select(tcs => tcs.Task); } Теперь можно будет сделать так: var tasks = newLst.ForEachAsync(n => DownloadImageAsyncWithRetry(n, retryCount), 100).ToList() И логику ретраинга просто впихнуть в DownloadImageAsyncWithRetry: public Task DownloadImageAsyncWithRetry(input) { var tsk = DownloadImageAsync(input); // Тут все зависит от того, каким именно образом определяется неудача. // Если это исключение, то вешаем ContinueWith, если это код возврата, // то проверяем его и пробуем повторить запрос. } Да, Parallel.For в этом случае идея - не очень, поскольку он предназначен прежде всего для CPU Intensive операций, а здесь явно IO Intensive. В этом случае логично сделать сам метод асинхронным, который будет дергать асинхронный API для загрузки картинок. З.Ы. Код ForEachAsync взять отсюда.

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

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