简体   繁体   English

在Linq中异步等待.TakeWhile

[英]Async Await In Linq .TakeWhile

I am trying to convert my foreach function to a linq function 我正在尝试将我的foreach函数转换为linq函数

here is my normal code [Works fine] 这是我的正常代码[效果很好]

    var tList = new List<Func<Task<bool>>> { Method1, Method2 };
    tList.Shuffle();

    int succeed = 0;
    foreach (var task in tList)
    {
        var result = await task();

        if(!result)
            break;

        succeed += 1;
    }

    MessageBox.Show(succeed == 1 ? "Loading complete." : "Something went wrong!");

And here is after converted to Linq [Giving 2 compiler errors] 这是转换为Linq之后的结果[产生2个编译器错误]

    var tList = new List<Func<Task<bool>>> { Method1, Method2 };
    tList.Shuffle();

    int succeed = tList.Select(async task => await task()).TakeWhile(result => result).Count();

    MessageBox.Show(succeed == 1 ? "Loading complete." : "Something went wrong!");

Errors 失误

  • Cannot convert lambda expression to delegate type 'System.Func,bool>' because some of the return types in the block are not implicitly convertible to the 无法将lambda表达式转换为委托类型'System.Func,bool>',因为该块中的某些返回类型不能隐式转换为
    delegate return type 委托返回类型
  • Cannot implicitly convert type 'System.Threading.Tasks.Task' to 'bool' 无法将类型'System.Threading.Tasks.Task'隐式转换为'bool'

I wonder why the compiler is giving those messages, so any help will be appreciated. 我不知道为什么编译器会给出这些消息,所以任何帮助将不胜感激。

Note : i also tried .TakeWhile(async result => await result) with that error 注意:我也尝试了.TakeWhile(async result => await result)与该错误

  • The return type of an async method must be void, Task, or Task T 异步方法的返回类型必须为void,Task或Task T

Method1 and Method2 if someone wanna them : Method1和Method2如果有人想要:

public async Task<bool> Method1()
{
    await Task.Delay(1000);
    Console.WriteLine("Method1");
    return false;
}

public async Task<bool> Method2()
{
    await Task.Delay(1000);
    Console.WriteLine("Method2");
    return true;
}

One possible solution 一种可能的解决方案

Use a TakeWhileAsync extension method like this: 使用如下所示的TakeWhileAsync扩展方法:

public static async Task<IEnumerable<T>> TakeWhileAsync<T>(this IEnumerable<Task<T>> tasks, Func<T, bool> predicate)
{
    var results = new List<T>();

    foreach (var task in tasks)
    {
        var result = await task;
        if (!predicate(result))
            break;

        results.Add(result);
    }

    return results;
}

There must be a more optimized solution but this is easy to read in my opinion. 必须有一个更优化的解决方案,但是我认为这很容易理解。 You iterate though the tasks, wait for each one to finish then do the same logic the regular TakeWhile method does. 您遍历任务,等待每个任务完成,然后执行与常规TakeWhile方法相同的逻辑。

First you need to actually call your methods: tList.Select(func => func() then apply the extension method over this newly created IEnumerable<Task<bool>> : tList.Select(func => func()).TakeWhileAsync(result => result) 首先,您需要实际调用您的方法: tList.Select(func => func()然后在新创建的IEnumerable <Task <bool >>上应用扩展方法: tList.Select(func => func()).TakeWhileAsync(result => result)

You then await this and count the result: (await tList.Select(func => func()).TakeWhileAsync(result => result)).Count() 然后,您等待它并计算结果:( (await tList.Select(func => func()).TakeWhileAsync(result => result)).Count()

Why your attempts did not work 为什么您的尝试不起作用

.Select(async task => await task()).TakeWhile(result => result)

Remember that an async function always returns a Task (or void). 请记住, 异步函数始终返回Task (或void)。 The lambda inside your select block returns an IEnumerable<Task<bool>> . select块中的lambda返回IEnumerable <Task <bool >> When you apply .TakeWhile on this, your TakeWhile predicate ( result => result ) also returns a Task<bool> , because every element of your sequence at that point is a Task<bool> ! 在此应用.TakeWhile时,您的TakeWhile谓词( result => result )也会返回Task <bool> ,因为此时序列中的每个元素都是Task <bool> That causes an error because it should return bool . 这会导致错误,因为它应该返回bool

.TakeWhile(async result => await result)

Same issue, your lambda returns a Task (because it is an async "method"), that will never work as a LINQ predicate. 同样的问题, 您的lambda返回一个Task (因为它是异步的“方法”),它将永远无法用作LINQ谓词。

.TakeWhile(result => result.Result)

This one (from Simon's answer) does compile, and will even work in some situations. (根据Simon的回答)该代码确实可以编译,甚至可以在某些情况下使用。 (Where the SynchronizationContext is not tied to a single thread, but that's another long topic.) The main takeaway however is that blocking on async code is dangerous and will potentially lead to deadlocks . (其中SynchronizationContext没有绑定到单个线程,但这是另一个漫长的话题。)然而,主要的要点是, 阻塞异步代码很危险,并且有可能导致死锁 Here is a great detailed article on this. 这是一篇非常详细的文章

There's a fundamental difference between the first and the second code (which do not do the same thing). 第一个代码和第二个代码之间有根本的区别(它们不做相同的事情)。

On the first code you are iterating over a list of tasks. 在第一个代码上,您正在遍历任务列表。 On the second you are just transforming each task into another - the return type of an async lambda will always be a task of something. 在第二个步骤中,您只是将每个任务转换为另一个任务-异步lambda的返回类型将始终是某种任务。

Because you area awaiting sequentially on each task they really running in sequence. 因为您要依次等待每个任务,所以它们实际上是按顺序运行的。 Is that what you intended to do? 那是你打算做的吗?

Try something like this (not tested): 尝试这样的事情(未经测试):

var tList = new List<Func<Task<bool>>> { Method1, Method2 };
tList.Shuffle();

int succeed = Task.WaitAll(tList.ToArray())
    .TakeWhile(result => result).Count();

MessageBox.Show(
    succeed == 1
    ? "Loading complete."
    : "Something went wrong!");

But that's still a strange piece of code. 但这仍然是一段奇怪的代码。

This is simply because you cannot convert Task<bool> to bool .. as the compiler has instructed. 这仅仅是因为您无法按照编译器的指示将Task<bool>转换为bool ..。

Change this: 更改此:

.TakeWhile(result => result)

..to this: ..为此:

.TakeWhile(result => result.Result)

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

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