Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/neo4j/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 等待异步谓词列表,但在第一个false时退出_C#_.net_Linq_Async Await_Enumeration - Fatal编程技术网

C# 等待异步谓词列表,但在第一个false时退出

C# 等待异步谓词列表,但在第一个false时退出,c#,.net,linq,async-await,enumeration,C#,.net,Linq,Async Await,Enumeration,想象一下下面的类: public class Checker { public async Task<bool> Check() { ... } } 现在,这将不会编译,因为Check()返回的是任务而不是bool 所以我的问题是:我怎样才能最好地列举出检查者的名单? 当检查器返回false时,我如何设置枚举的快捷方式? (我认为All()已经做到了这一点)All并没有考虑到async(就像AllLINQ),所以您需要自己实现它: async Task<bool>

想象一下下面的类:

public class Checker
{
   public async Task<bool> Check() { ... }
}
现在,这将不会编译,因为
Check()
返回的是
任务
而不是
bool

所以我的问题是:我怎样才能最好地列举出检查者的名单? 当检查器返回
false
时,我如何设置枚举的快捷方式?
(我认为
All()
已经做到了这一点)

All
并没有考虑到
async
(就像All
LINQ
),所以您需要自己实现它:

async Task<bool> CheckAll()
{
    foreach(var checker in checkers)
    {
        if (!await checker.Check())
        {
            return false;
        }
    }

    return true;
}
你可以

checkers.All(c => c.Check().Result);
但这将同步运行任务,这可能非常慢,具体取决于
Check()
的实现

当检查器返回false时,我如何设置枚举的快捷方式

这将按完成顺序检查任务的结果。因此,如果task#5是第一个完成的任务,并且返回false,那么不管其他任务如何,该方法都会立即返回false。较慢的任务(1、2等)永远不会被检查

public static async Task<bool> AllAsync(this IEnumerable<Task<bool>> source)
{
    var tasks = source.ToList();

    while(tasks.Count != 0)
    {
        var finishedTask = await Task.WhenAny(tasks);

        if(! finishedTask.Result)
            return false;

        tasks.Remove(finishedTask);
    }

    return true;
}
“异步序列”总是会引起一些混乱。例如,不清楚您想要的语义是否为:

  • 同时启动所有检查,并在检查完成时对其进行评估
  • 每次开始一项检查,按顺序进行评估
  • 还有第三种可能性(同时启动所有检查,并按顺序对它们进行评估),但在这种情况下,这是愚蠢的

    我建议对异步序列使用Rx。它给了你很多选择,学习起来有点困难,但它也迫使你准确地思考你想要什么

    以下代码将同时启动所有检查,并在检查完成时对其进行评估:

    IObservable<bool> result = checkers.ToObservable()
        .SelectMany(c => c.Check()).All(b => b);
    
    bool all = await checkers.ToObservable().SelectMany(c => c.Check()).All(b => b);
    
    它首先将跳棋序列转换为可观测序列,然后连接这些序列(一次启动一个序列)

    如果您不想太多地使用可观察对象,也不想干扰订阅,您可以直接等待它们。例如,调用所有检查器上的
    Check
    ,并在其完成时评估结果:

    IObservable<bool> result = checkers.ToObservable()
        .SelectMany(c => c.Check()).All(b => b);
    
    bool all = await checkers.ToObservable().SelectMany(c => c.Check()).All(b => b);
    

    以下是一个全功能测试程序,按照dcastro的步骤进行:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace AsyncCheckerTest
    {
        public class Checker
        {
            public int Seconds { get; private set; }
    
            public Checker(int seconds)
            {
                Seconds = seconds;
            }
    
            public async Task<bool> CheckAsync()
            {
                await Task.Delay(Seconds * 1000);
                return Seconds != 3;
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                var task = RunAsync();
                task.Wait();
    
                Console.WriteLine("Overall result: " + task.Result);
                Console.ReadLine();
            }
    
            public static async Task<bool> RunAsync()
            {
                var checkers = new List<Checker>();
                checkers
                    .AddRange(Enumerable.Range(1, 5)
                    .Select(i => new Checker(i)));
    
                return await checkers
                                .Select(c => c.CheckAsync())
                                .AllAsync();
            }
        }
    
        public static class ExtensionMethods
        {
            public static async Task<bool> AllAsync(this IEnumerable<Task<bool>> source)
            {
                var tasks = source.ToList();
    
                while (tasks.Count != 0)
                {
                    Task<bool> finishedTask = await Task.WhenAny(tasks);
    
                    bool checkResult = finishedTask.Result;
    
                    if (!checkResult)
                    {
                        Console.WriteLine("Completed at " + DateTimeOffset.Now + "...false");
                        return false;
                    }
    
                    Console.WriteLine("Working... " + DateTimeOffset.Now);
                    tasks.Remove(finishedTask);
                }
    
                return true;
            }
        }
    }
    

    请注意,当达到退出条件时,整个评估结束,而不等待其余部分完成。

    作为一种更开箱即用的替代方案,这似乎是并行运行任务,并在第一次失败后不久返回:

    var allResult = checkers
        .Select(c => Task.Factory.StartNew(() => c.Check().Result))
        .AsParallel()
        .All(t => t.Result);
    


    我对TPL和PLINQ不太感兴趣,所以请随时告诉我这有什么问题。

    第二个选项将使委托
    异步无效
    ,这几乎应该是无效的。好吧,它无论如何都不起作用,因为我刚刚测试过它。RX是几个“我知道这对我有好处,但我的大脑会受伤”区域之一。特别是如果您在它上面添加RxUI;-)我不熟悉
    Rx
    ,我不确定
    Rx
    如何等待
    checkers.ToObservable()语句中的结果。选择many(c=>c.Check())。All(b=>b)
    。我可以看到人们可以等待一个可观察的,但不确定这一切是如何发生的。其他答案假设只有一个选项。@NedStoyanov:当您
    等待
    一个可观察对象时,代码(异步)等待该可观察对象完成并返回最后一个值。我意识到我可以使用进度来更新控制台,这样在生产中会更好。我试图说明这样一个事实:async/await即使在没有“当前同步上下文”可恢复的情况下也能工作。你为什么先
    选择
    ,然后
    AllAsync
    ,而不是像@I3arnon在他的回答中那样组合它们呢?@Vegar没有特别的原因,但我想,如果你要写一个(
    AllAsync())
    )或其他(
    AllAsync(Funcok)。尽管我喜欢“流畅的合成”,但我还是喜欢尽可能短的序列。-@Vegar为了最大的灵活性/可读性,您可以始终使用这两种扩展方法:)只需稍微更改签名,并将第一行更改为
    var tasks=source.Select(谓词).ToList();我怀疑一旦ThreadPool耗尽,您的程序就会阻塞。线程池最好在计划计算密集型工作时使用。如果每次异步检查花费的时间相对较长,那么这将比其他实现花费的时间长得多。@GregC公平,我知道它似乎太简单:)使用
    async
    的目的就是不不必要地阻塞线程,这正是它的作用。而且
    StartNew()
    没有任何意义,如果它只是
    Select(c=>c.Check())
    它也可以工作,并且不会阻塞任何线程。@svick我试过了,它只是无限期地挂起。实际上,这是并行运行线程的。这不是和Task.WhenAll的语义不同吗?我认为dcastro和Cleary的答案更快。@nawfal可能,是的。这取决于是否允许单个检查同时运行,这不一定是真的,但我不记得4年前的上下文。
    IObservable<bool> result = checkers.Select(c => c.Check().ToObservable())
        .Concat().All(b => b);
    
    bool all = await checkers.ToObservable().SelectMany(c => c.Check()).All(b => b);
    
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace AsyncCheckerTest
    {
        public class Checker
        {
            public int Seconds { get; private set; }
    
            public Checker(int seconds)
            {
                Seconds = seconds;
            }
    
            public async Task<bool> CheckAsync()
            {
                await Task.Delay(Seconds * 1000);
                return Seconds != 3;
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                var task = RunAsync();
                task.Wait();
    
                Console.WriteLine("Overall result: " + task.Result);
                Console.ReadLine();
            }
    
            public static async Task<bool> RunAsync()
            {
                var checkers = new List<Checker>();
                checkers
                    .AddRange(Enumerable.Range(1, 5)
                    .Select(i => new Checker(i)));
    
                return await checkers
                                .Select(c => c.CheckAsync())
                                .AllAsync();
            }
        }
    
        public static class ExtensionMethods
        {
            public static async Task<bool> AllAsync(this IEnumerable<Task<bool>> source)
            {
                var tasks = source.ToList();
    
                while (tasks.Count != 0)
                {
                    Task<bool> finishedTask = await Task.WhenAny(tasks);
    
                    bool checkResult = finishedTask.Result;
    
                    if (!checkResult)
                    {
                        Console.WriteLine("Completed at " + DateTimeOffset.Now + "...false");
                        return false;
                    }
    
                    Console.WriteLine("Working... " + DateTimeOffset.Now);
                    tasks.Remove(finishedTask);
                }
    
                return true;
            }
        }
    }
    
    Working... 6/27/2014 1:47:35 AM -05:00
    Working... 6/27/2014 1:47:36 AM -05:00
    Completed at 6/27/2014 1:47:37 AM -05:00...false
    Overall result: False
    
    var allResult = checkers
        .Select(c => Task.Factory.StartNew(() => c.Check().Result))
        .AsParallel()
        .All(t => t.Result);