本文介绍了当枚举器集合未强制转换为列表时,IAsyncEnumerator.Current返回null的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述 29岁程序员,3月因学历无情被辞! 第一个功能旨在使linq能够安全地并行执行lambda函数(甚至是异步void的函数).因此您可以进行collection.AsParallel().ForAllASync(async x => await x.Action).第二个功能旨在使您能够并行组合和执行多个IAsyncEnumerable,并尽快返回其结果.我有以下代码: 公共静态异步任务ForAllAsync< TSource>(此ParallelQuery< TSource>来源,Func< TSource,Task>选择器诠释?maxDegreeOfParallelism = null){int maxAsyncThreadCount = maxDegreeOfParallelism ??Math.Min(System.Environment.ProcessorCount,128);使用SemaphoreSlim节流阀= new SemaphoreSlim(maxAsyncThreadCount,maxAsyncThreadCount);IEnumerable< Task>任务= source.Select(异步输入=>{等待节流阀.WaitAsync().ConfigureAwait(false);尝试{await选择器(输入).ConfigureAwait(false);}最后{节流阀.Release();}});等待Task.WhenAll(tasks).ConfigureAwait(true);}公共静态异步IAsyncEnumerable< T>ForAllAsync< TSource,T>(此ParallelQuery< TSource>来源,Func TSource,IAsyncEnumerableT.选择器,诠释?maxDegreeOfParallelism = null,[EnumeratorCancellation] CancellationToken cancelledToken =默认值)其中T:new(){IEnumerable<(IAsyncEnumerator< T> bool)>枚举数=source.Select(x => (selector.Invoke(x).GetAsyncEnumerator(cancellationToken), true)).ToList();同时(enumerators.Any()){等待枚举器.AsParallel().ForAllAsync(异步e => e.Item2 =(等待e.Item1.MoveNextAsync()),maxDegreeOfParallelism).ConfigureAwait(false);foreach(枚举器中的var枚举器){收益回报率enumerator.Item1.Current;}枚举数=枚举数.}} 如果我删除了"ToList()",从第二个函数开始,收益回报率开始以enumerator.Item1返回null.尽管enumerator.Item2(MoveNextAsync()的结果)为true,但Current往往为null.为什么?解决方案这是延迟执行的经典案例.每次在非物化的 IEnumerable 上调用评估方法时,它都会执行物化 IEnumerable 的工作.在这种情况下,这将重新调用选择器并创建等待GetAsyncEnumerator调用的任务的新实例.通过调用 .ToList(),您可以实现IEnumerable.没有它,每次调用 .Any(),调用 ForAllAsync()以及在 foreach 循环中都会实现./p>相同的行为可以像这样最少地重现: var枚举= new [] {1} .Select(_ => Task.Delay(10));等待Task.WhenAll(enumerable);Console.WriteLine(enumerable.First().IsCompleted);//错误的枚举=枚举.ToList();等待Task.WhenAll(enumerable);Console.WriteLine(enumerable.First().IsCompleted);//真的 在第一次调用 enumerable.First()时,我们最终得到的任务实例不同于之前一行中等待的任务实例.在第二个调用中,我们使用相同的实例,因为Task已经实现了.The first function is designed to enable linq to execute lambda functions safely in parallel (even the async void ones).So you can do collection.AsParallel().ForAllASync(async x => await x.Action).The second function is designed to enable you to combine and execute multiple IAsyncEnumerables in parallel and return their results as quick as possible.I have the following code: public static async Task ForAllAsync<TSource>( this ParallelQuery<TSource> source, Func<TSource, Task> selector, int? maxDegreeOfParallelism = null) { int maxAsyncThreadCount = maxDegreeOfParallelism ?? Math.Min(System.Environment.ProcessorCount, 128); using SemaphoreSlim throttler = new SemaphoreSlim(maxAsyncThreadCount, maxAsyncThreadCount); IEnumerable<Task> tasks = source.Select(async input => { await throttler.WaitAsync().ConfigureAwait(false); try { await selector(input).ConfigureAwait(false); } finally { throttler.Release(); } }); await Task.WhenAll(tasks).ConfigureAwait(true); } public static async IAsyncEnumerable<T> ForAllAsync<TSource, T>( this ParallelQuery<TSource> source, Func<TSource, IAsyncEnumerable<T>> selector, int? maxDegreeOfParallelism = null, [EnumeratorCancellation]CancellationToken cancellationToken = default) where T : new() { IEnumerable<(IAsyncEnumerator<T>, bool)> enumerators = source.Select(x => (selector.Invoke(x).GetAsyncEnumerator(cancellationToken), true)).ToList(); while (enumerators.Any()) { await enumerators.AsParallel() .ForAllAsync(async e => e.Item2 = (await e.Item1.MoveNextAsync()), maxDegreeOfParallelism) .ConfigureAwait(false); foreach (var enumerator in enumerators) { yield return enumerator.Item1.Current; } enumerators = enumerators.Where(e => e.Item2); } }If I remove the "ToList()" from the second function, yield return starts to return null as enumerator.Item1.Current tends to be null, despite enumerator.Item2 (the result from MoveNextAsync()) being true.Why? 解决方案 This is a classic case of deferred execution. Every time you invoke an evaluating method on a non-materialized IEnumerable<>, it does the work to materialize the IEnumerable. In this case that's re-invoking your selector and creating new instances of the tasks that await the GetAsyncEnumerator calls.With the call to .ToList() you materialize the IEnumerable. Without it, materialization occurs with with every call to .Any(), the call to ForAllAsync(), and at your foreach loop.The same behavior can be reproduced minimally like this:var enumerable = new[] { 1 }.Select(_ => Task.Delay(10));await Task.WhenAll(enumerable);Console.WriteLine(enumerable.First().IsCompleted); // Falseenumerable = enumerable.ToList();await Task.WhenAll(enumerable);Console.WriteLine(enumerable.First().IsCompleted); // TrueIn the first call to enumerable.First(), we end up with a different task instance than the one that we awaited in the line before it.In the second call, we're using the same instance because the Task was already materialized into a List. 这篇关于当枚举器集合未强制转换为列表时,IAsyncEnumerator.Current返回null的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 上岸,阿里云!
08-15 15:30