使用Rx排队操作我不希望在特定时间之前执行?

时间:2011-08-25 17:28:59

标签: c# .net-3.5 threadpool system.reactive

总结:我有一个Web应用程序,可以在业务对象上执行工作流,有时需要在步骤之间有意等待几秒或几分钟。我希望(也许通过Rx.NET)改进这些工作流程的执行,这样我就不会耗尽ThreadPool,并在系统负载很重时使网站无响应。

工作流程的一个非常简化的版本是:

  1. 创建对象
  2. 从系统A
  3. 将数据加载到其中
  4. 将此数据发布到系统B
  5. 如果系统A关闭,我的应用程序会等待并稍后重试。等待时间是在GMail不断升级的重试延迟之后建模的:等待1秒,每次后续重试加倍(最多1小时)。该应用程序痴迷地将状态保存到数据库,因此如果整个应用程序爆炸,当它重新启动时,它将恢复它停止的所有工作流程。

    目前(请保持温和)工作流程中的每个步骤都是通过调用ThreadPool.QueueUserWorkItem来排队调用Thread.Sleep的方法来执行,如果需要进行上述重试延迟,则实际执行该步骤。

    如果系统运行良好(没有错误),它可以轻松处理我们抛出的所有流量,并且ThreadPool可以很好地管理所有这些工作流实例的并行执行。但是如果系统B停机一段时间,重试计数会因此延迟增长,很快ThreadPool就会充满所有正在使用的线程,导致网站对新请求没有响应。

    基本上我想将所有这些待处理的工作流抛出到按顺序排列的队列中(上次执行时间+所需的重试延迟)。尽管阅读了很多关于Rx并且很兴奋的事情,但我从来没有机会使用它,但似乎它可能是一种有用的方法来处理这个问题。如果Rx能够在他们准备开火的时候神奇地管理吐出这些物体,那么它似乎就是

    1. 大大简化和澄清这种逻辑,
    2. 防止浪费使用大量99%的时间正在睡觉的线程
    3. 对Rx新手的任何指导都会非常感激,即使只是为了解释为什么这对Rx来说实际上不是一个好的用例。

3 个答案:

答案 0 :(得分:4)

在这种情况下,我可能会坚持使用您当前的解决方案,因为这一点:

  

该应用程序痴迷地将状态保存到数据库中,因此如果整个应用程序爆炸,当它重新启动时,它将恢复所有停止的工作流程。

启动时通过反序列化“恢复”管道(即x.Where().Select().Timeout().Bla())非常棘手。

如果你没有尝试对整个流程进行建模,只是交易位(即从A加载,发送到B),很难为你提供更详细的解决方案而没有更多信息,它可能与Rx实际上相当不错。

无论如何,解决线程池耗尽的方法是通过System.Threading.Timer类,它告诉线程池在排队新项之前等待超时。

答案 1 :(得分:2)

你肯定要适应:

public IDisposable StartProcess<T>(Action<T> load, Action<T> post) where T : new()
{
    return StartProcess(TimeSpan.FromSeconds(1), new T())
                .Do(load)
                .Subscribe(post);
}

private IObservable<long> StartProcess<T>(TimeSpan span, T obj) where T : new()
{
    Observable
        .Interval(span)
        .OnErrorResumeNext(Observable.Defer(() => StartProcess(IncreaseSpan(span), obj)))
        .Concat(Observable.Defer(() => StartProcess(TimeSpan.FromSeconds(1), new T())));
}

private TimeSpan IncreaseSpan(TimeSpan span)
{
    return TimeSpan.FromSeconds(span.TotalSeconds < 1800? span.TotalSeconds * 2 : 3600);
}

现在我更倾向于加载实例化并填充对象而不是明确地执行它,因为函数式编程会阻止可变性,您可能希望load实际转到数据库并恢复您提到的状态。 / p>

我不确定你是否想要保留状态对象以防postload的调用崩溃而你需要适应,因为目前它会保留状态{是否{如果load崩溃,那么{1}}或post会崩溃,并且会在没有新状态的情况下再次致电load

可能不是您想要做的事情。

我没有测试代码,但Rx适合你想要做的事情。

答案 2 :(得分:1)

在Rx论坛上查看此帖子。对于您想要解决的问题,非常方便的运算符:http://social.msdn.microsoft.com/Forums/en-US/rx/thread/af43b14e-fb00-42d4-8fb1-5c45862f7796/

Rx是处理这类(特别是)问题的好方法,因为你可以拥有异步函数/ observables并将通用运算符应用于所描述的Retry运算符。