C# 可观察。计时器或带任务的TPL。延迟
我有一个要求,在10秒的初始延迟后,我需要每10分钟执行一次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. 我知道如何使用第三方物流做
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))
:)