Process List asynchronously with SemaphoreSlim for throttling extension method I've got some methods that follow a similar pattern, and wanted to abstract some of the logic away into an extension method. Specifically, I am processing lists of objects with the TPL, and throttling that processing with a SemaphoreSlim.

public static Task ValidateProxiesAsync(IList&lt;Proxy&gt; proxies, IList&lt;ProxyJudge&gt; judges, IList&lt;ProxyTest&gt; tests)
{
    return Task.Run(async () =&gt;
    {
        await ValidateTestsAsync(judges.ToList&lt;IProxyTest&gt;());
        await ValidateTestsAsync(tests.ToList&lt;IProxyTest&gt;());
        var semaphore = new SemaphoreSlim(100);
        var tasks = new List&lt;Task&gt;();
        foreach (var proxy in proxies)
        {
            await semaphore.WaitAsync();
            tasks.Add(Task.Run(() =&gt;
            {
                proxy.Status = proxy.TestValidity(judges[0]);
                semaphore.Release();
            }));
        }
        await Task.WhenAll(tasks);
    });
}

public static Task ValidateTestsAsync(IList&lt;IProxyTest&gt; tests)
{
    return Task.Run(async () =&gt;
    {
        var semaphore = new SemaphoreSlim(100);
        var tasks = new List&lt;Task&gt;();
        foreach (var test in tests)
        {
            await semaphore.WaitAsync();
            tasks.Add(Task.Run(() =&gt;
            {
                test.Status = test.TestValidity();
                semaphore.Release();
            }));
        }
        await Task.WhenAll(tasks);
    });
}


I wrote this extension method to reduce the LOC needed to do this. But I wanted to make sure I didn't overlook something. Perhaps there is a better solution that someone on here could suggest (TPL Dataflow?).

Extension method:

    public static Task ProcessWithSemaphoreAsync&lt;T&gt;(this IList&lt;T&gt; list, Action&lt;T&gt; action, int maxConcurrency)
    {
        return Task.Run(async () =&gt; {
            var semaphore = new SemaphoreSlim(maxConcurrency);
            var tasks = new List&lt;Task&gt;();
            foreach (var item in list)
            {
                await semaphore.WaitAsync();
                tasks.Add(Task.Run(() =&gt;
                {
                    action.Invoke(item);
                    semaphore.Release();
                }));
            }
            await Task.WhenAll(tasks);
        });
    }
The only thing I can see is that you should dispose the semaphore:

using (var semaphore = new SemaphoreSlim(maxConcurrency))
{
  var tasks ....
}


And you could generalize to IEnumerable&lt;T&gt; instead of IList&lt;T&gt;



A simple solution using Parallel.ForEach() could be:

public static void ProcessWithParallelAsync&lt;T&gt;(this IEnumerable&lt;T&gt; list, Action&lt;T&gt; action, int maxConcurrency)
{
  ParallelOptions options = new ParallelOptions();
  options.MaxDegreeOfParallelism = maxConcurrency;
  Parallel.ForEach(list, options, action);
}


Or you could wrap it in a Task if you like:

public static Task ProcessWithParallelAsync&lt;T&gt;(this IEnumerable&lt;T&gt; list, Action&lt;T&gt; action, int maxConcurrency)
{
  return Task.Run(() =&gt;
  {
    ParallelOptions options = new ParallelOptions();
    options.MaxDegreeOfParallelism = maxConcurrency;

    Parallel.ForEach(list, options, action);
  });
}