在Subscribe中调用Task.Factory.StartNew(async()=> {})一般是不确定的吗?

时间:2014-11-11 21:06:38

标签: c# asynchronous system.reactive orleans

我遇到需要使用自定义调度程序来运行任务(这些需要是任务)而调度程序没有设置同步上下文的情况(因此没有ObserveOnSubscribeOn,{ {1}}等我收集)。以下是我最终如何做到这一点。现在,我想知道,我不确定这是否是进行异步调用和等待结果的最合适方式。这是正确的还是有更强大或惯用的方式?

SynchronizationContextScheduler

如果不需要等待怎么办?

< edit:我找到了一个具体的,以及我正在做的here的简化示例。基本上我在奥尔良使用Rx,上面的代码是我要做的事情的简单说明。虽然我也对这种情况感兴趣。

最终代码 事实证明,在奥尔良的背景下,这有点棘手。我不知道如何使用var orleansScheduler = TaskScheduler.Current; var someObservable = ...; someObservable.Subscribe(i => { Task.Factory.StartNew(async () => { return await AsynchronousOperation(i); }, CancellationToken.None, TaskCreationOptions.None, orleansScheduler); }); ,这将是我想要使用的东西。问题是通过使用它,ObserveOn永远不会被调用。代码:

Subscribe

此外,指向Codeplex Orleans forums的相关主题的链接。

2 个答案:

答案 0 :(得分:4)

对于任何现代代码,我强烈建议不要使用StartNew。它确实有一个用例,但它非常罕见。

如果您必须使用自定义任务计划程序,建议您使用ObserveOn TaskPoolScheduler,并使用TaskFactory包装程序围绕您的计划程序构建var factory = new TaskFactory(customScheduler); var rxScheduler = new TaskPoolScheduler(factory); someObservable.ObserveOn(rxScheduler)... 。这是一个满口的,所以这是一般的想法:

SelectMany

然后,您可以使用async void为源流中的每个事件启动异步操作。

另一种不太理想的解决方案是使用ActionBlock进行订阅"事件"。这是可以接受的,但你必须注意你的错误处理。作为一般规则,不允许异常传播出异步void方法。

还有第三种方法,可以将observable挂钩到TPL Dataflow块中。像{{1}}这样的块可以指定其任务调度程序,Dataflow自然可以理解异步处理程序。请注意,默认情况下,Dataflow块会一次将处理限制为单个元素。

答案 1 :(得分:3)

一般来说,不是订阅执行,而是将任务参数投影到任务执行中并且只为结果订阅更好/更惯用。这样你就可以在下游进一步组建Rx。

e.g。给出一个随机任务,如:

static async Task<int> DoubleAsync(int i, Random random)
{
    Console.WriteLine("Started");
    await Task.Delay(TimeSpan.FromSeconds(random.Next(10) + 1));
    return i * 2;
}

然后你可能会这样做:

void Main()
{
    var random = new Random();

    // stream of task parameters
    var source = Observable.Range(1, 5);

    // project the task parameters into the task execution, collect and flatten results
    source.SelectMany(i => DoubleAsync(i, random))

          // subscribe just for results, which turn up as they are done
          // gives you flexibility to continue the rx chain here
          .Subscribe(result => Console.WriteLine(result),
                    () => Console.WriteLine("All done."));
}