C# 如何使用异步倒计时事件,而不是收集任务并等待它们?

C# 如何使用异步倒计时事件,而不是收集任务并等待它们?,c#,asynchronous,system.reactive,C#,Asynchronous,System.reactive,我有以下代码: var tasks = await taskSeedSource .Select(taskSeed => GetPendingOrRunningTask(taskSeed, createTask, onFailed, onSuccess, sem)) .ToList() .ToTask(); if (tasks.Count == 0) { return; } if (tasks.Contains(null)) { tasks =

我有以下代码:

var tasks = await taskSeedSource
    .Select(taskSeed => GetPendingOrRunningTask(taskSeed, createTask, onFailed, onSuccess, sem))
    .ToList()
    .ToTask();

if (tasks.Count == 0)
{
    return;
}

if (tasks.Contains(null))
{
    tasks = tasks.Where(t => t != null).ToArray();
    if (tasks.Count == 0)
    {
        return;
    }
}

await Task.WhenAll(tasks);
其中,
taskSeedSource
是一个反应性的可观察对象。这段代码可能有很多问题,但我至少看到两个问题:

  • 我正在收集任务,但我可以不用它
  • 无论如何,返回的任务列表可能包含null,即使
    GetPendingOrRunningTask
    是一个
    async
    方法,因此从不返回
    null
    。我无法理解它为什么会发生,因此我不得不在不了解问题原因的情况下进行辩护
  • 我希望使用框架中的
    AsyncCountdownEvent
    ,而不是收集任务然后等待它们

    因此,我可以将倒计时事件传递给
    GetPendingOrRunningTask
    ,它将立即递增并在等待内部逻辑完成后返回之前发出信号。但是,我不明白如何将倒计时事件集成到monad中(这是一个反应性术语,不是吗?)

    正确的方法是什么

    编辑

    伙计们,让我们忘记返回列表中神秘的空值。假设一切都是绿色的,代码是绿色的

    var tasks = await taskSeedSource
        .Select(taskSeed => GetPendingOrRunningTask(taskSeed, ...))
        .ToList()
        .ToTask();
    
    await Task.WhenAll(tasks);
    
    现在的问题是我如何在倒计时事件中做到这一点?那么,假设我有:

    var c = new AsyncCountdownEvent(1);
    

    async Task GetPendingOrRunningTask(AsyncCountdownEvent c,T taskSeed,…)
    {
    c、 AddCount();
    尝试
    {
    等待。。。。
    }
    捕获(异常exc)
    {
    //异常已被处理
    }
    c、 信号();
    }
    
    我的问题是,我不再需要返回的任务。收集并等待这些任务以获得所有工作项结束的时刻,但现在可以使用倒计时事件来指示工作结束的时间

    我的问题是,我不知道如何将其整合到反应链中。本质上,
    GetPendingOrRunningTask
    可以是
    async void
    。我被困在这里了

    编辑2


    @Servy是正确的,您需要在源代码处解决null
    任务问题。没有人愿意回答一个问题,即如何解决一个违反了您自己定义的方法契约但尚未提供检查源的问题

    至于收集任务的问题,如果您的方法返回一个通用的
    任务
    ,则使用
    Merge
    很容易避免:

    然而,不幸的是,对于非泛型
    任务
    没有官方的
    合并
    重载,但这很容易定义:

    public static IObservable<Unit> Merge(this IObservable<Task> sources)
    {
      return sources.Select(async source =>
      {
        await source.ConfigureAwait(false);
        return Unit.Default;
      })
      .Merge();
    }
    
    公共静态IObservable合并(此IObservable源)
    {
    返回源。选择(异步源=>
    {
    等待源。配置等待(false);
    返回单位。默认值;
    })
    .Merge();
    }
    
    与您的声明相反,您永远不会有空任务,也不需要针对它们编写代码。也没有理由对零任务进行特殊处理<如果集合中没有任务,则代码>所有
    都将立即完成
    AsyncCountdownEvent
    会使代码更复杂,而不是更简单。我认为代码中的ToTask是打字错误。否则就无法编译了,不是吗?关于
    whalll
    ,您可能是对的,但我对空值并没有妄想。我的单元测试失败,因为列表中包含空值。我不能解释它,但我不是发明它的。@标记如果你有空值,那么事情就很不对劲,你应该找出根本原因,而不是试图围绕它编码。@标记:听起来你的模拟框架可能返回了一个
    null
    任务。试着升级你的模拟框架(除了VS存根,没有现代版本能做到这一点)。所以,基本上你是说我根本不需要任何倒计时事件。是的,你试过我的例子吗?(如果您使用的是非通用的
    任务
    ),您只需要我展示的新的
    合并
    扩展)您使用的是哪种合并方法?我之所以问这个问题,是因为我没有看到没有参数的合并扩展方法。它是
    IObservable
    的扩展方法。我相信它是在RX2.0中引入的。您使用的是Rx的哪个版本?2.2.5.0能否告诉我包含此方法的扩展类的完整类型名称?
    await taskSeedSource
      .Select(taskSeed => GetPendingOrRunningTask(taskSeed, createTask, onFailed, onSuccess, sem))
      .Where(task => task != null)  // According to you, this shouldn't be necessary.
      .Merge();
    
    public static IObservable<Unit> Merge(this IObservable<Task> sources)
    {
      return sources.Select(async source =>
      {
        await source.ConfigureAwait(false);
        return Unit.Default;
      })
      .Merge();
    }