I encoded it under the assumption that asynchronous and parallel processing would be the same
Asynchronous processing and parallel processing are completely different. If you don't understand the difference, I think you should first read about it (for example, what is the relationship between asynchronous and parallel programming in C #? ).
Now what you want to do is actually not that simple, because you want to process a large collection asynchronously with a certain degree of parallelism (8). With synchronous processing, you can use Parallel.ForEach() (along with ParallelOptions to adjust the degree of parallelism), but there is no simple alternative that will work with async .
In your code, this is complicated by the fact that you expect everything to run in the user interface thread. (Although ideally, you should not access the user interface directly from your calculations. Instead, you should use IProgress , which would mean that the code should no longer be executed in the user interface thread.)
Probably the best way to do this in .Net 4.5 is to use the TPL data stream. Its ActionBlock does exactly what you want, but it can be quite verbose (because it is more flexible than what you need). Therefore, it makes sense to create a helper method:
public static Task AsyncParallelForEach<T>( IEnumerable<T> source, Func<T, Task> body, int maxDegreeOfParallelism = DataflowBlockOptions.Unbounded, TaskScheduler scheduler = null) { var options = new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = maxDegreeOfParallelism }; if (scheduler != null) options.TaskScheduler = scheduler; var block = new ActionBlock<T>(body, options); foreach (var item in source) block.Post(item); block.Complete(); return block.Completion; }
In your case, you will use it as follows:
await AsyncParallelForEach( threads, async url => await DownloadUrl(url), 8, TaskScheduler.FromCurrentSynchronizationContext());
Here DownloadUrl() is an async Task method that processes a single URL (the body of your loop), 8 is a degree of parallelism (probably should not be a constant in real code) and FromCurrentSynchronizationContext() provides code execution in the user interface stream.