最近,我遇到了一个限制异步/等待调用线程的示例。在分析并使用了机器上的代码后,我想到了做相同事情的略有不同的方式。我不确定的是,引擎盖下发生的事情几乎是相同的,还是有任何细微的差异值得注意?
这是基于原始示例的代码:
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(5);
public async Task CallThrottledTasks()
{
var tasks = new List<Task>();
for (int count = 1; count <= 20; count++)
{
await _semaphore.WaitAsync();
tasks.Add(Task.Run(async () =>
{
try
{
int result = await LongRunningTask();
Debug.Print(result.ToString());
}
finally
{
_semaphore.Release();
}
}));
}
await Task.WhenAll(tasks);
Debug.Print("Finished CallThrottledTasks");
}
这是我对相同代码的看法:
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(5);
public async Task CallThrottledTasks()
{
var tasks = new List<Task>();
for (int count = 1; count <= 20; count++)
{
await _semaphore.WaitAsync();
tasks.Add(LongRunningTask().ContinueWith(t =>
{
try
{
int result = t.Result;
Debug.Print(result.ToString());
}
finally
{
_semaphore.Release();
}
}));
}
await Task.WhenAll(tasks);
Debug.Print("Finished CallThrottledTasks");
}
我可能还很遥远,但似乎Task.Run方法正在创建一个任务以运行LongRunningTask(),然后添加了一个连续内容以打印结果,而我的方法却绕过了Task.Run创建的任务,并且更加精简因此。这是准确的还是我在这里偏离了基础?
最佳答案
它并不精简,只是一点点。通常,我避免在ContinueWith
代码中使用async
,因为await
更加整洁,并且具有更多async
友好的默认语义。首先针对开发人员时间进行优化,然后针对其他考虑因素进行优化。
您的代码确实稍微改变了语义:在原始代码中,LongRunningTask
是从线程池上下文中执行的,而在您的代码中,它是从CallThrottledTasks
上下文中执行的。另外,您的代码不会干净地传播LongRunningTask
的异常; Task<T>.Result
将异常包装在AggregateException
中,而await
将不进行任何包装。