一次只运行一组任务
比方说,我有100个任务需要10秒钟的时间。 现在我只想一次只运行10个,就像10个任务中的1个完成另一个任务,直到所有任务完成。
现在我总是使用ThreadPool.QueueUserWorkItem()
来完成这个任务,但是我读过这样做是不好的做法,而应该使用Tasks来代替。
我的问题是,我没有find一个很好的例子,所以你可以让我开始如何实现这个目标与任务?
SemaphoreSlim maxThread = new SemaphoreSlim(10); for (int i = 0; i < 115; i++) { maxThread.Wait(); Task.Factory.StartNew(() => { //Your Works } , TaskCreationOptions.LongRunning) .ContinueWith( (task) => maxThread.Release() ); }
TPL Dataflow非常适合这样的事情。 您可以很容易地创build一个100%的asynchronous版本的Parallel.Invoke
:
async Task ProcessTenAtOnce<T>(IEnumerable<T> items, Func<T, Task> func) { ExecutionDataflowBlockOptions edfbo = new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 10 }; ActionBlock<T> ab = new ActionBlock<T>(func, edfbo); foreach (T item in items) { await ab.SendAsync(item); } ab.Complete(); await ab.Completion; }
你有几个select。 初学者可以使用Parallel.Invoke
:
public void DoWork(IEnumerable<Action> actions) { Parallel.Invoke(new ParallelOptions() { MaxDegreeOfParallelism = 10 } , actions.ToArray()); }
这是一个替代选项,它将更难以正确运行10个任务(尽pipe线程池中处理这些任务的线程数可能不同),并且返回一个Task
指示何时结束,而不是直到完成才阻塞。
public Task DoWork(IList<Action> actions) { List<Task> tasks = new List<Task>(); int numWorkers = 10; int batchSize = (int)Math.Ceiling(actions.Count / (double)numWorkers); foreach (var batch in actions.Batch(actions.Count / 10)) { tasks.Add(Task.Factory.StartNew(() => { foreach (var action in batch) { action(); } })); } return Task.WhenAll(tasks); }
如果你没有MoreLinq,那么对于Batch
函数,这是我更简单的实现:
public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> source, int batchSize) { List<T> buffer = new List<T>(batchSize); foreach (T item in source) { buffer.Add(item); if (buffer.Count >= batchSize) { yield return buffer; buffer = new List<T>(); } } if (buffer.Count >= 0) { yield return buffer; } }
我想用我能想到的最简单的解决scheme,就像我认为使用TPL一样:
string[] urls={}; Parallel.ForEach(urls, new ParallelOptions() { MaxDegreeOfParallelism = 2}, url => { //Download the content or do whatever you want with each URL });