首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用IAsyncEnumerable构建TaskCompletionSource

使用IAsyncEnumerable构建TaskCompletionSource
EN

Stack Overflow用户
提问于 2020-04-30 13:45:20
回答 2查看 377关注 0票数 1

我有一个方法,它接受一个IEnumerable并返回它,该方法使用产率运算符进行转换。要转换可枚举元素的一个元素,我需要首先知道另一个元素的值。因此,我想使用TaskCompletionSource来创建类似于“承诺”的东西。

这里的问题是,如果"a“是第一个TestFieldA的值,那么这段代码会导致死锁。一种解决方案是在将枚举传递到方法之前对枚举进行排序--在这种情况下,完全不需要TaskCompletionSource。不过,我想知道,如果不这样做,是否可以做到。我也知道,这可以用一些LINQ查询,但这将需要枚举输入几次,我想避免。

这就是我想要达到的目标。(只有在第一个TestFieldA == "a"起作用时才能工作)

代码语言:javascript
复制
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;
        }
    }
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-04-30 15:32:01

一个想法可以是返回一个可枚举的任务,而不是一个IAsyncEnumerable。就像这样:

代码语言:javascript
复制
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;
    }
}

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

代码语言:javascript
复制
// Creates a new collection of tasks that complete in order.
public static List<Task<T>> OrderByCompletion<T>(this IEnumerable<Task<T>> @this);

如果需要,还可以从这里获取源代码。

票数 1
EN

Stack Overflow用户

发布于 2020-04-30 14:37:45

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

此时,您对所有排队的项进行排序,使用现在找到的值,然后依次生成每个项。然后使用所需的TestFieldA值生成项。

您是如何从那里开始的还不太清楚,因为我不知道如果( a)找到另一个a项和b)如果没有找到a项,您想要做什么。

这里不需要Task(CompletionSource)、asyncIAsyncEnumerable --除非您能够访问时间机器,否则在找到a值之前不能产生任何结果。

还请记住,迭代器依赖于调用者反复请求新的项目来取得进展--在每个yield上暂停直到他们完成为止。因此,在极端情况下,考虑尽早尝试yield项是危险的,如果生成的项中有任何关于它们的"Task-like“;调用方可能决定对其中一个条目进行await,而不是继续您需要它们的枚举。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/61524854

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档