1

我有一个接受 anIEnumerable并返回使用 yield 运算符转换的方法。要转换可枚举的一个元素,我首先需要知道另一个元素的值。因此,我想到了使用 aTaskCompletionSource来创建类似“promise”的东西。

这里的问题是,如果“a”以外的任何值是 first 的值,则此代码会导致死锁TestFieldA。一种解决方案是在将枚举传递给方法之前对其进行排序——在这种情况下,完全不需要TaskCompletionSource。但是,我想知道是否可以在没有这个的情况下完成。我也知道这可以通过一些 LINQ 查询来完成,但这需要多次枚举输入,我想避免这种情况。

这就是我想要达到的目标。(仅适用于第一个TestFieldA == "a"

class Test
{
    public string TestFieldA {get;set;}
    public int TestFieldB {get;set;}
}


private async IAsyncEnumerable<Test> Transform(IEnumerable<Test> inputEnumerable)
{
    var tcs = new TaskCompletionSource<int>();

    foreach(var input in inputEnumerable)
    {
        if (input.TestFieldA == "a")
        {
            tcs.SetResult(input.TestFieldB);
            yield return input;
        }
        else
        {
            input.TestFieldB -= await tcs.Task;
            yield return input;
        }
    }
}
4

2 回答 2

2

你目前的计划似乎取决于能否及时返回。我建议只将不合适的项目存储在队列中(而不是产生它们),直到找到合适的TestFieldA有价值的项目。

此时,您将所有排队的项目出列,使用现在找到的值并依次产生每个项目。然后产生具有所需TestFieldA值的项目。

你如何从那里开始有点不清楚,因为我不知道如果 a)a找到另一个项目和 b) 如果没有a找到项目你想做什么。

不需要Task( CompletionSource) ,async或者IAsyncEnumerable在这里 - 在你找到你的价值之前你不能产生任何东西a- 除非你可以使用时间机器。


还要记住,迭代器依赖于让他们的调用者反复请求新项目以取得进展——你在每个项目上都暂停,yield直到他们这样做。yield因此,如果产出的物品有任何“ Task-like”的东西,那么考虑提前尝试物品将是极端冒险的;调用者可能会决定await使用一个,而不是继续您需要他们进行的枚举。

于 2020-04-30T14:37:45.917 回答
1

一个想法可能是返回一个可枚举的任务,而不是一个IAsyncEnumerable. 像这样的东西:

private IEnumerable<Task<Test>> Transform(IEnumerable<Test> source)
{
    var tcs = new TaskCompletionSource<int>(
        TaskCreationOptions.RunContinuationsAsynchronously);

    foreach (var item in source)
    {
        if (item.TestFieldA == "a")
        {
            tcs.TrySetResult(item.TestFieldB);
        }
        yield return TransformItemAsync(item);
    }

    async Task<Test> TransformItemAsync(Test input)
    {
        var value = await tcs.Task.ConfigureAwait(false);
        input.TestFieldB -= value;
        return input;
    }
}

如果调用者试图按顺序等待每个任务,这仍然会产生死锁问题。为了解决这个问题,调用者应该有办法以某种方式等待任务完成。Nito.AsyncEx在 Stephen Cleary 的库中有类似的东西,扩展方法OrderByCompletion

// Creates a new collection of tasks that complete in order.
public static List<Task<T>> OrderByCompletion<T>(this IEnumerable<Task<T>> @this);

如果需要,您也可以从此处获取源代码。

于 2020-04-30T15:32:01.257 回答