![](/img/trans.png)
[英]HttpClient.SendAsync using the thread-pool instead of async IO?
[英]Limit parallelism of an Async method and not block a Thread-Pool thread
我有一个异步方法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();
}
}
更优雅的方式是什么?
TaskScheduler
标准Task
API TaskScheduler
仅对CPU绑定工作有用。 你的工作不是使用线程。 它使用IO完成端口,这意味着您的网络呼叫根本不包含任何线程。 没有办法让TaskScheduler
用于IO操作。
如果您还不确定:.NET中的异步IO基于使用TaskCompletionSource
,它不会以最轻微的方式绑定到线程或调度程序。
SemaphoreSlim
是正确的方法。 或者,创建一个ServicePoint
并设置其最大并发性。 仅适用于HTTP
请求。
请注意,如果您发现自己使用Wait
那么您应该犹豫并思考您正在做什么。 通常,这是一个错误。
public static async Task RequestAsyncCool()
{
await Task.Factory.StartNew(async () => {
await RequestInternalAsync();
},
CancellationToken.None,
TaskCreationOptions.DenyChildAttach,
TaskScheduler.Current);
}
你真的不应该Wait
任务。 请参阅https://www.google.com/search?q=task+wait+deadlock
你看过TPL DataFlow了吗? 这可能只是你的事情......
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.