#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 взять отсюда.
Комментариев нет:
Отправить комментарий