嵌套async lambda未等待

本文关键字:等待 lambda async 嵌套 | 更新日期: 2023-09-27 18:04:18

下面的代码不返回它正在迭代的整个集合。每次运行时返回的数组具有任意长度。怎么了?

public async Task<IHttpActionResult> GetClients()
{
    var clientInfoCollection = new ConcurrentBag<ClientInfoModel>();
    await _client.Iterate(async (client) =>
    {
        clientInfoCollection.Add(new ClientInfoModel
        {
            name = client.name,
            userCount = await _user.Count(clientId)
        });
    });
    return Ok(clientInfoCollection.ToArray());
}
下面的代码使用新的异步MongoDB c#驱动程序
public async Task Iterate(Action<TDocument> processor)
{
    await _collection.Find<TDocument>(_ => true).ForEachAsync(processor);
}

嵌套async lambda未等待

您看到任意数量的值的原因是Iterate接收类型为Action<T>的委托,这相当于async void,有效地使其成为"即发即弃"的执行风格。

内部方法实际上并不知道async委托已经传递给它,因此它迭代集合而不实际异步等待每个项目完成。

您需要做的是使方法参数成为Func<TDocument, Task>类型的委托,并使用ForEachAsync的适当重载:

public Task Iterate(Func<TDocument, Task> processor)
{
    return _collection.Find<TDocument>(_ => true).ForEachAsync(processor);
}

你可以在这里看到源代码:

public static async Task ForEachAsync<TDocument>(
                    this IAsyncCursor<TDocument> source, 
                    Func<TDocument, int, Task> processor,
                    CancellationToken cancellationToken = default(CancellationToken))
{
    Ensure.IsNotNull(source, "source");
    Ensure.IsNotNull(processor, "processor");
    // yes, we are taking ownership... assumption being that they've
    // exhausted the thing and don't need it anymore.
    using (source)
    {
        var index = 0;
        while (await source.MoveNextAsync(cancellationToken).ConfigureAwait(false))
        {
            foreach (var document in source.Current)
            {
                await processor(document, index++).ConfigureAwait(false);
                cancellationToken.ThrowIfCancellationRequested();
            }
        }
    }
}

创建线程,并设置它们。从那里你不知道会发生什么。但是你的代码的下一步是返回,所以你是在赌线程的执行速度会比主线程快。

在正常的线程场景中,您将加入正在向包中添加物品的线程。其中join是等待其他线程执行的线程,因此仍然是异步的,但是等待在所有事情完成之前返回。

这里有很好的解释:http://www.dotnetperls.com/thread-join