简体   繁体   English

循环内的异步/等待是否会造成瓶颈?

[英]Does async/await inside a loop create a bottleneck?

Lets say i have the following code for example: 可以说我有以下示例代码:

private async Task ManageClients()
{
    for (int i =0; i < listClients.Count; i++)
    {
        if (list[i] == 0)
            await DoSomethingWithClientAsync();
        else
            await DoOtherThingAsync();
    }

    DoOtherWork();
}

My questions are: 我的问题是:

1. Will the for() continue and process other clients on the list ?, or it will await untill it finishes one of the tasks. 1. for()将继续并处理列表上的其他客户端吗?或者它将等待直到完成一项任务。

2. Is even a good practice to use async/await inside a loop? 2.在循环内使用async / await甚至是一种好习惯吗?

3. Can it be done in a better way? 3.可以采用更好的方法吗?

I know it was a really simple example, but I'm trying to imagine what would happen if that code was a server with thousands of clients. 我知道这是一个非常简单的示例,但是我试图想象如果该代码是具有数千个客户端的服务器会发生什么。

In your code example, the code will "block" when the loop reaches await , meaning the other clients will not be processed until the first one is complete. 在您的代码示例中,当循环达到await时,代码将“阻塞”,这意味着直到第一个客户端完成,其他客户端才会得到处理。 That is because, while the code uses asynchronous calls, it was written using a synchronous logic mindset. 这是因为,尽管代码使用异步调用,却是使用同步逻辑思维方式编写的。

An asynchronous approach should look more like this: 异步方法应如下所示:

private async Task ManageClients()
{
    var tasks = listClients.Select( client => DoSomethingWithClient() );
    await Task.WhenAll(tasks);
    DoOtherWork();
}

Notice there is only one await , which simultaneously awaits all of the clients, and allows them to complete in any order. 请注意,只有一个await ,它同时等待所有客户端,并允许它们以任何顺序完成。 This avoids the situation where the loop is blocked waiting for the first client. 这样可以避免循环阻塞等待第一个客户端的情况。

If a thread that is executing an async function is calling another async function, this other function is executed as if it was not async until it sees a call to a third async function. 如果正在执行异步功能的线程正在调用另一个异步功能,则该另一个功能就好像不是异步的一样执行,直到看到对第三个异步功能的调用。 This third async function is executed also as if it was not async. 第三个异步函数也被执行,就好像它不是异步的一样。

This goes on, until the thread sees an await. 这继续进行,直到线程看到等待。

Instead of really doing nothing, the thread goes up the call stack, to see if the caller was not awaiting for the result of the called function. 线程并没有真正执行任何操作,而是向上调用堆栈,以查看调用方是否未等待被调用函数的结果。 If not, the thread continues the statements in the caller function until it sees an await. 如果不是,则线程继续调用方函数中的语句,直到看到等待状态为止。 The thread goes up the call stack again to see if it can continue there. 线程再次进入调用堆栈,以查看是否可以继续执行。

This can be seen in the following code: 可以在以下代码中看到:

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;

You can even call several sub-procedures without awaiting: 您甚至可以在不等待的情况下调用多个子过程:

 var taskDoSomething = DoSomethingAsync(...);
 var taskDoSomethingElse = DoSomethingElseAsync(...);
 // we are here both tasks are awaiting
 DoSomethingElse();

Once you need the results of the tasks, if depends what you want to do with them. 一旦您需要任务的结果,是否取决于您要如何处理它们。 Can you continue processing if one task is completed but the other is not? 如果一项任务完成但另一项任务没有完成,您可以继续处理吗?

var someResult = await taskDoSomething;
ProcessResult(someResult);
var someOtherResult = await taskDoSomethingelse;
ProcessBothResults(someResult, someOtherResult);

If you need the result of all tasks before you can continue, use Task.WhenAll: 如果需要所有任务的结果才能继续,请使用Task.WhenAll:

Task[] allTasks = new Task[] {taskDoSomething, taskDoSomethingElse);
await Task.WhenAll(allTasks);
var someResult = taskDoSomething.Result;
var someOtherResult = taskDoSomethingElse.Result;
ProcessBothResults(someResult, someOtherResult);

Back to your question 回到您的问题

If you have a sequence of items where you need to start awaitable tasks, it depends on whether the tasks need the result of other tasks or not. 如果有一系列项目需要启动等待的任务,则取决于这些任务是否需要其他任务的结果。 In other words can task[2] start if task[1] has not been completed yet? 换句话说,如果任务[1]尚未完成,任务[2]是否可以启动? Do Task[1] and Task[2] interfere with each other if they run both at the same time? 如果Task [1]和Task [2]同时运行,它们是否会相互干扰?

If they are independent, then start all Tasks without awaiting. 如果它们是独立的,则无需等待即可启动所有任务。 Then use Task.WhenAll to wait until all are finished. 然后使用Task.WhenAll等待所有操作完成。 The Task scheduler will take care that not to many tasks will be started at the same time. 任务计划程序将注意不要同时启动许多任务。 Be aware though, that starting several tasks could lead to deadlocks. 但是请注意,启动多个任务可能会导致死锁。 Check carefully if you need critical sections 仔细检查是否需要关键部分

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();

This was the scenario where all Tasks where independent: no task needed the other to be completed before it could start. 在这种情况下,所有任务都是独立的:没有任务需要先完成另一个任务才能开始。 However if you need Task[2] to be completed before you can start Task[3] you should await: 但是,如果您需要先完成Task [2],然后才能启动Task [3],则应等待:

foreach(var client in clients)
{
    if (list[i] == 0)
        await DoSomethingWithClientAsync());
    else
        await DoOtherThingAsync();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM