C# 可观察。计时器或带任务的TPL。延迟

C# 可观察。计时器或带任务的TPL。延迟,c#,task-parallel-library,system.reactive,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. 我知道如何使用第三方物流做

我有一个要求,在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.
我知道如何使用第三方物流做到这一点。我可以使用task.Delay并执行
somethod
启动任务,然后在每次完成后(
ContinueWith TaskStatus.RanToCompletion
),我创建一个新任务并再次执行
somethod

我的问题是,使用
可观察的计时器
,这可能吗?类似于

Observable.Timer(TimeSpan.FromSeconds(10), TimeSpan.FromMinutes(10))
这段代码的问题是,如果
SomeMethod
需要15分钟,我将运行两个不同的
SomeMethod
实例,这是我不想要的。我希望10分钟计时器在
somethod
完成后启动。这是否可能使用
可观察的
或我应该留在TPL


EDIT:忘了提到我想要
SomeMethod
在它自己的线程中运行

假设
SomeMethod
在完成时发出
OnCompleted
事件,我们可以将其作为
可观察的

//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());
//如果某个未完成的方法符合.NET事件模式
var completedObservable=Observable.FromEventPattern(
e=>this.OnCompleted+=e,
e=>this.OnCompleted+=e);
//订阅未完成的事件
var repeatDisposable=completedObservable.Subscribe(=>
可观察计时器(时间跨度从分钟(10))
.Subscribe(=>SomeMethod());
//启动条件
var=可观察计时器(时间跨度从秒(10))
.Subscribe(=>SomeMethod());

您应该对使用
Observable.Timer
进行更多的调查。它的工作原理几乎就像你想要的一样,直接从盒子里拿出来

关于Rx,需要了解的一件重要事情是,它保证您永远不会获得单个订阅的并发执行。虽然Rx可能会启用各种多线程方案,但它始终会序列化订阅

因此,以这个可观察订阅为例:

Observable
    .Timer(TimeSpan.FromSeconds(10.0), TimeSpan.FromSeconds(2.0))
    .Timestamp()
    .Subscribe(x =>
    {
        Thread.Sleep(5000);
        Console.WriteLine(x.ToString());
    });
我已经创建了一个observable,它将等待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秒发射一次。生成操作符的2和观察者的5


很明显,您可以输入所需的时间。

我的意思是
ContinueWith task status.ranto completion
而不是
ContinueWith OnCompleted
。修正了这个问题。该方法不实现任何完成事件。不过,这是一个很好的解决方案。向上投票。我不确定用已完成的事件开始污染方法是不是一个好主意。我也会小心在订阅中使用订阅,部分原因是一次性相关问题,部分原因是你的订阅应该在那里处理steam的输出,而不是启动新的steam。感谢@Chris的评论,你对如何改进订阅有什么建议吗?一般来说,如果您想将观察值串在一起,那么使用SelectMany创建1个管道是最好的方法。然后,您可以拥有一个使用此管道的订阅块。这在评论中看起来并不漂亮,但类似这样的内容:(从Obs1中的result1()从Obs2中的result2()选择result2)。订阅(x=>{…},ex=>{…})感谢您的回答。我做了完全相同的事情,但没有得到结果,因为我错过了问题中的一个重要细节。我想在它自己的独立线程中以预期的行为运行SomeMethod。我猜它涉及到订阅(或观察?),但我真的被它弄糊涂了。我对Rx有非常基本的了解。@Yogesh-您可能应该在
ObserveOn
中使用
EventLoopScheduler
的一个新实例,或者直接添加到
。Generate(…)
。您的
Generate
解决方案与
SubscribeOn
完美配合使用(
定时器
不起作用)。我还阅读了有关使用
TaskPoolScheduler.Default
LongRunningTask
问题,但它似乎对
ObserveOn
产生了更大的影响,因为它尝试为每个观察创建一个线程。尽管我仍在分析,但我认为解决方案应该可以正常工作,没有任何异常。如果它确实产生了与内存相关的问题,我仍然有
TaskPoolScheduler.Default.DisableOptimizations(typeof(IsSchedulerLongRunning))
:)