带有Task.Delay的Observable.Timer或TPL

时间:2015-08-31 09:21:21

标签: c# task-parallel-library system.reactive

我有一个要求,在10秒的初始延迟之后,我需要每10分钟执行一次SomeMethod,但是在SomeMethod完成后应该开始执行10分钟计时器。这是一个粗略的例子:

Start Task 00:00:00
(10 second delay)
SomeMethod executed at 00:00:10 (takes 15 minutes)
(10 minute delay)
SomeMethod executed at 00:25:10 
... and so on.

我知道如何使用TPL做到这一点。我可以使用Task.Delay启动任务并执行SomeMethod,然后在每次完成(ContinueWith TaskStatus.RanToCompletion)后,我创建一个新任务并再次执行SomeMethod

我的问题是,使用Observable.Timer是否可行?有点像...

Observable.Timer(TimeSpan.FromSeconds(10), TimeSpan.FromMinutes(10))

此代码存在的问题是,如果SomeMethod需要15分钟,我将运行两个不同的SomeMethod个实例,我不想要这样做。我希望10分钟计时器在 SomeMethod完成后启动。这可能是使用Observable还是我应该留在TPL?

编辑:忘记提及我希望SomeMethod在其自己的主题中运行。

2 个答案:

答案 0 :(得分:6)

您应该对使用Observable.Timer进行更多调查。它的工作方式几乎就像你想要的一样。

了解Rx的一个重要事项是,它可以保证您永远不会同时执行单个订阅。虽然Rx可能会启用各种多线程场景,但它始终会对订阅进行序列化。

所以,以这个可观察的订阅为例:

Observable
    .Timer(TimeSpan.FromSeconds(10.0), TimeSpan.FromSeconds(2.0))
    .Timestamp()
    .Subscribe(x =>
    {
        Thread.Sleep(5000);
        Console.WriteLine(x.ToString());
    });

我创建了一个可观察的观察点,它将等待10秒钟开始发射值,然后尝试每2秒发出一次值。

然后我添加了.Timestamp()来准确记录产生值的时间。

最后,我订阅了一个迫使5秒线程睡眠的观察者。

这里输出前4个值:

0@2015-08-31 10:44:34 +00:00
1@2015-08-31 10:44:39 +00:00
2@2015-08-31 10:44:44 +00:00
3@2015-08-31 10:44:49 +00:00

您会注意到值之间的差距是5秒。这非常接近你想要的。 Rx看到两秒已经过去并立即执行下一个值。

但是还有另一个Rx运算符可以完全按照您的需要运行 - .Generate(...)。这是一个非常强大的运算符,用于生成各种可观察的流。

您想要像这样使用它:

Observable
    .Generate(0, x => true, x => x + 1, x => x,
        x => x == 0 ? TimeSpan.FromSeconds(10.0) : TimeSpan.FromSeconds(2.0))
    .Timestamp()
    .Subscribe(x =>
    {
        Thread.Sleep(5000);
        Console.WriteLine(x.ToString());
    });

在这种情况下,它可以按照您想要的方式工作。这是前十个值:

0@2015-08-31 10:48:27 +00:00
1@2015-08-31 10:48:34 +00:00
2@2015-08-31 10:48:41 +00:00
3@2015-08-31 10:48:48 +00:00
4@2015-08-31 10:48:55 +00:00
5@2015-08-31 10:49:02 +00:00
6@2015-08-31 10:49:09 +00:00
7@2015-08-31 10:49:16 +00:00
8@2015-08-31 10:49:23 +00:00
9@2015-08-31 10:49:30 +00:00

它每7秒发射一次。来自generate运算符的2和来自观察者的5。

你显然可以投入你需要的时间。

答案 1 :(得分:2)

假设SomeMethod在完成时发出OnCompleted个事件,我们可以将其写为Observable

//If SomeMethod OnCompleted conforms to .NET Event Pattern
var completedObservable = Observable.FromEventPattern<OnCompletedEventArgs>(
            e => this.OnCompleted += e,
            e => this.OnCompleted += e);

//Subscribe to OnCompleted events
var repeatDisposable = completedObservable.Subscribe(_ => 
                                Observable.Timer(TimeSpan.FromMinutes(10))
                                          .Subscribe(_ => SomeMethod()));

//Start condition
var starterDisposable = Observable.Timer(TimeSpan.FromSeconds(10))
                                  .Subscribe(_ => SomeMethod());