【发布时间】:2014-05-08 19:06:05
【问题描述】:
我有一个异步方法RequestInternalAsync() 向外部资源发出请求,并且想编写一个包装器方法,通过减少并行度来限制对该方法的并发异步请求数。
想到的第一个选项是TaskScheduler,并发有限(LimitedConcurrencyLevelTaskScheduler、ConcurrentExclusiveSchedulerPair 等)。
但要使用自定义调度程序运行任务,我必须使用仅接受 Action<> 的 TaskFactory 启动任务,即我不能通过不阻塞额外线程来等待内部方法的执行来做到这一点.
第二个选项是SemaphoreSlim,它完成了它的工作,但在这种情况下,我自己实现了节流,而不是使用TaskScheduler。
static void Main(string[] args)
{
// TESTING 1
var task1 = Task.WhenAll(Enumerable.Range(1, 10).Select(i => RequestAsyncBad()));
task1.Wait();
// TESTING 2
var task2 = Task.WhenAll(Enumerable.Range(1, 10).Select(i => RequestAsyncBetter()));
task2.Wait();
}
private static Task RequestInternalAsync()
{
return Task.Delay(500);
}
解决方案 #1:
private static readonly ConcurrentExclusiveSchedulerPair _concurrentPair
= new ConcurrentExclusiveSchedulerPair(TaskScheduler.Default, 2);
public static Task RequestAsyncBad()
{
// Dumb: Because TaskFactory doesn't provide an overload which accepts another task, only action.
// As result, we blocking a thread to just wait until the inner task finishes.
return Task.Factory.StartNew(() => RequestInternalAsync().Wait(),
CancellationToken.None, TaskCreationOptions.DenyChildAttach, _concurrentPair.ConcurrentScheduler);
}
解决方案 #2(更好):
private static readonly SemaphoreSlim _semaphore = new SemaphoreSlim(2);
public static async Task RequestAsyncBetter()
{
// Here we don't waste thread-pool thread on waiting for a completion of inner task,
// but instead of using TaskScheduler, implementing a hand-made stuff with semaphore.
await _semaphore.WaitAsync().ConfigureAwait(false);
try
{
await RequestInternalAsync();
}
finally
{
_semaphore.Release();
}
}
更优雅的方法是什么?
- 重用TPL的标准
TaskAPI和TaskScheduler - 并且不阻塞额外的线程
【问题讨论】:
标签: c# task-parallel-library async-await