此问题在此处已有答案:
Async await in linq select(8个答案)
两个月前关门了。
阅读完这篇帖子:正在并行嵌套等待。ForEach
我试着做了以下几点:
private static async void Solution3UsingLinq()
{
var ids = new List<string>() { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" };
var customerTasks = ids.Select(async i =>
{
ICustomerRepo repo = new CustomerRepo();
var id = await repo.getCustomer(i);
Console.WriteLine(id);
});
}
我不明白为什么,我觉得有一个僵局,但我不确定...
2条答案
按热度按时间wko9yo5t1#
所以在方法的末尾,
customerTasks
包含一个IEnumerable<Task>
,这个IEnumerable<Task>
还没有被枚举。Select
中的任何代码都不会运行。当创建这样的任务时,立即具体化序列可能更安全,以降低双重枚举的风险(并意外创建第二批任务)。
因此:
现在...如何处理您的任务列表?您需要等待它们全部完成...
您可以使用
Task.WhenAll
执行此操作:您可以更进一步,在
Select
语句中从async
委托返回一个值,这样您就得到了一个IEnumerable<Task<Customer>>
。然后可以使用different overload of
Task.WhenAll
:当然,可能还有更有效的方法来一次性获得几个客户,但这是针对另一个问题。
相反,如果您希望按顺序执行异步任务,则:
i7uaboj42#
增加:
对于LINQ语句实际执行的时间,尤其是Where语句,似乎有些混乱。
我创建了一个小程序来显示实际访问源数据的时间。
添加结束
您必须了解大多数LINQ函数的惰性。
惰性LINQ函数只会更改
IEnumerable.GetEnumerator()
在您开始枚举时返回的Enumerator
。因此,只要您调用惰性LINQ函数,查询就不会执行。只有当你开始枚举的时候,查询才会被执行。当你调用
foreach
,或者像ToList()
,Any()
,FirstOrDefault()
,Max()
这样的LINQ函数的时候,枚举才会开始。在每个LINQ函数的注解部分都描述了该函数是否为惰性函数。您还可以通过检查返回值来查看该函数是否为惰性函数。如果它返回IEnumerable<...>(或IQueryable),则尚未枚举LINQ。
这种惰性的好处是,只要您只使用惰性函数,更改LINQ表达式就不会耗费时间。只有当您使用非惰性函数时,才必须注意其影响。
例如,如果由于排序、分组、数据库查询等原因,提取序列的第一个元素需要很长时间才能计算,请确保不要开始多次枚举(=不要对同一序列多次使用非惰性函数)
不要在家里这样做
假设您有以下查询
此查询仅包含惰性LINQ函数。在所有这些语句之后,尚未访问
todoLists
。但是,一旦得到结果序列的第一个元素,就必须访问所有元素(可能不止一次),以便按优先级对它们进行分组,计算所涉及的工作时间总数,并按优先级降序对它们进行排序。
Any()和First()都是这种情况:
在这种情况下,最好使用正确的函数:
回到您问题
Enumerable.Select
语句只为您创建了一个IEnumerable
对象。您忘记了枚举它。此外,您还多次构建了CustomerRepo。这是有意的吗?
附加:何时执行LINQ语句?
我创建了一个小程序来测试何时执行LINQ语句,特别是当执行Where语句时。
返回IEnumerable的函数:
一个使用此枚举的程序,该枚举使用一个老式的枚举器
在调试过程中,我可以看到**第一次调用MoveNext()时执行GetNumbers。**GetNumbers()一直执行到第一个yield return语句。
每次调用MoveNext()时,都会执行yield返回之后的语句,直到执行下一个yield返回。
更改代码,以便使用foreach、Any()、FirstOrDefault()、ToDictionary等访问枚举数,这表明对这些函数的调用是实际访问原始源的时间。
调试显示原始源**从开头开始枚举两次。**因此,这样做确实是不明智的,尤其是当您需要做很多工作来计算第一个元素(GroupBy、OrderBy、数据库访问等)时。