C# 循环内的async/await是否会造成瓶颈?
例如,假设我有以下代码:C# 循环内的async/await是否会造成瓶颈?,c#,multithreading,loops,async-await,C#,Multithreading,Loops,Async Await,例如,假设我有以下代码: private async Task ManageClients() { for (int i =0; i < listClients.Count; i++) { if (list[i] == 0) await DoSomethingWithClientAsync(); else await DoOtherThingAsync(); } DoOther
private async Task ManageClients()
{
for (int i =0; i < listClients.Count; i++)
{
if (list[i] == 0)
await DoSomethingWithClientAsync();
else
await DoOtherThingAsync();
}
DoOtherWork();
}
private async Task ManageClients()
{
for(int i=0;i
我的问题是:
1。for()是否会继续处理列表中的其他客户端?或者
将等待,直到它完成其中一项任务
2.在循环中使用async/await是否是一种好的做法
3.可以用更好的方法吗
我知道这是一个非常简单的示例,但我正在尝试想象如果该代码是一个具有数千个客户端的服务器,会发生什么情况。在您的代码示例中,当循环到达
wait
时,代码将“阻塞”,这意味着在第一个客户端完成之前不会处理其他客户端。这是因为,虽然代码使用异步调用,但它是使用同步逻辑思维编写的
异步方法应该更像这样:
private async Task ManageClients()
{
var tasks = listClients.Select( client => DoSomethingWithClient() );
await Task.WhenAll(tasks);
DoOtherWork();
}
请注意,只有一个
wait
,它同时等待所有客户端,并允许它们以任何顺序完成。这避免了循环在等待第一个客户端时被阻塞的情况。如果执行异步函数的线程正在调用另一个异步函数,则在看到对第三个异步函数的调用之前,该另一个函数将被视为非异步函数来执行。第三个异步函数的执行也如同它不是异步函数一样
这将继续,直到线程看到等待
线程不是什么都不做,而是向上移动调用堆栈,查看调用方是否在等待被调用函数的结果。如果没有,线程将继续调用函数中的语句,直到看到等待。线程再次进入调用堆栈,查看是否可以继续
这可以在以下代码中看到:
var taskDoSomething = DoSomethingAsync(...);
// because we are not awaiting, the following is done as soon as DoSomethingAsync has to await:
DoSomethingElse();
// from here we need the result from DoSomethingAsync. await for it:
var someResult = await taskDoSomething;
您甚至可以调用多个子过程,而无需等待:
var taskDoSomething = DoSomethingAsync(...);
var taskDoSomethingElse = DoSomethingElseAsync(...);
// we are here both tasks are awaiting
DoSomethingElse();
一旦您需要任务的结果,if取决于您想对其执行什么操作。如果一项任务已完成,但另一项任务未完成,是否可以继续处理
var someResult = await taskDoSomething;
ProcessResult(someResult);
var someOtherResult = await taskDoSomethingelse;
ProcessBothResults(someResult, someOtherResult);
如果需要所有任务的结果才能继续,请使用Task.whall:
Task[] allTasks = new Task[] {taskDoSomething, taskDoSomethingElse);
await Task.WhenAll(allTasks);
var someResult = taskDoSomething.Result;
var someOtherResult = taskDoSomethingElse.Result;
ProcessBothResults(someResult, someOtherResult);
回到你的问题
如果您有一系列项目需要开始等待的任务,则这取决于这些任务是否需要其他任务的结果。换句话说,如果任务[1]尚未完成,任务[2]能否启动?如果任务[1]和任务[2]同时运行,它们是否相互干扰
如果它们是独立的,则无需等待即可启动所有任务。然后使用Task.whalll
等待所有任务完成。任务调度器将注意不要同时启动太多任务。但是要注意,启动多个任务可能会导致死锁。如果需要关键部位,请仔细检查
var clientTasks = new List<Task>();
foreach(var client in clients)
{
if (list[i] == 0)
clientTasks.Add(DoSomethingWithClientAsync());
else
clientTasks.Add(DoOtherThingAsync());
}
// if here: all tasks started. If desired you can do other things:
AndNowForSomethingCompletelyDifferent();
// later we need the other tasks to be finished:
var taskWaitAll = Task.WhenAll(clientTasks);
// did you notice we still did not await yet, we are still in business:
MontyPython();
// okay, done with frolicking, we need the results:
await taskWaitAll;
DoOtherWork();
您希望发生的事情可能重复?如果您记得只应在代码没有任何用处的地方使用
await
,直到右边的任务完成,这是否回答了您的问题?单词await
清楚地告诉您,它将等待任务完成。此外,如果DoOtherwork不需要任务的结果,您可以将其放在等待之前,以便任务和DoOtherwork并行运行,而不是顺序运行。如果DoSomethingWithClient中的代码影响程序的状态,这可能会改变程序的行为。这不是更异步,只是不同而已。原始代码也非常异步,与调用者交错。更好的方法在很大程度上取决于环境和任务是否独立。问题是DoSomethingWithClient()是一个I/O操作,因此理想情况下,我希望尽快发送结果,但也希望同时处理10个客户端I/O。这是一种更好的方法吗?这种方法允许在等待第一个I/O操作完成之前启动所有I/O操作。是的,如果你想要一个更短的整体执行时间,它可能会更好。非常感谢你的详细回答,它帮助我理清了一些事情。但是,如果DoSomethingWithClientAsync是一个I/O操作,而les说10个客户端同时需要结果,那么等待的时间是什么呢?所有这些都是最好的方法吗?
foreach(var client in clients)
{
if (list[i] == 0)
await DoSomethingWithClientAsync());
else
await DoOtherThingAsync();
}