【问题标题】:Calling a method in specific interval在特定时间间隔调用方法
【发布时间】:2020-02-24 08:53:31
【问题描述】:

我需要在特定的时间间隔内运行一个方法。如何通过c#实现它

我写了一个代码,但它没有重复。它只执行一次。 谁能向我解释什么是可观察和取消令牌。如何使用它

IObservable<long> observable = Observable.Interval(TimeSpan.FromHours(1));
System.Threading.CancellationTokenSource source = new System.Threading.CancellationTokenSource();
observable.Subscribe(x =>
{
    System.Threading.Tasks.Task task = sampleMethod();
    task.Start();
}, source.Token);

【问题讨论】:

  • 一个澄清:您是否正在寻找在重复间隔中调用方法的最简单方法并且您不小心找到了这个IObservable 示例,或者您实际上想使用IObservable
  • 如果您按计划运行某项任务,我很想让操作系统使用其本地工具(例如 Windows 的任务计划程序)运行任务。
  • 你能解释我什么时候使用IObservable ,如果有更好的解决方案@Groo
  • @Iceberg - 对于这类事情,使用IObservable&lt;&gt; 是理想的。它可以是一种超级干净的编码方式。
  • @Enigmativity IObsevable&lt;&gt; 将如何工作?

标签: c# observable system.reactive


【解决方案1】:

您可以通过多种方式实现这一目标。最简单的一种是这样的:

public async Task RunScheduleJob(CancellationToken token)
{
  while(!token.IsCancellationRequest)
  {
    YourMethod();
    await Task.Delay(TimeSpan.FromHours(1), token)
  }
}

PS。对于这样一个简单的工作,您不需要使用 Reactive Extensions 或任何其他外部库。你可以通过使用Task 实例来实现它

【讨论】:

  • 你能解释我什么时候使用observable
  • @ijas 你不需要使用它。不要过于复杂
  • @olegl 感谢帮助,为什么CancellationToken作为异步任务的参数,我不知道cancellationtoken是什么
  • CancellationToken 对于优雅地停止所有调用和 I/O 操作很重要。总是通过它是一个非常好的做法
  • 这是一个糟糕的解决方案。它不能被取消。如果CancellationToken 令牌说它应该停止,则while (true) 本身应该是break
【解决方案2】:

Rx 是处理这种情况的绝佳方式。超级干净。

您问题中代码的最基本重写是这样的:

IObservable<Unit> observable =
    from i in Observable.Interval(TimeSpan.FromHours(1))
    from t in Observable.FromAsync(() => sampleMethod())
    select t;

IDisposable subscription = observable.Subscribe();

不需要取消令牌,因为您只需致电 subscription.Dispose() 即可停止此订阅。

但是,如果您的 sampleMethod() 调用长时间运行,则调用 subscription.Dispose() 将停止 observable,但任务将运行完成。如果您也特别想停止任务,那么对任务的调用将需要获取一个取消令牌并使用它来自行停止。

你的调用代码是这样的:

IObservable<Unit> observable =
    from i in Observable.Interval(TimeSpan.FromHours(1))
    from t in Observable.FromAsync(ct => sampleMethod(ct))
    select t;

唯一的区别是FromAsync 调用将CancellationToken 传递给sampleMethod 调用,当您调用subscription.Dispose() 时该调用将被取消。

【讨论】:

  • 你能解释一下这是如何工作的吗?使用 Observable 比使用带有延迟任务的 while 循环有什么优势
  • @Iceberg - Observables 比使用带有await Task.Delaywhile 循环要强大得多。您可以进行各种连接、过滤、投影等操作。
【解决方案3】:

Observable.Interval(TimeSpan.FromHours(1)).SelectMany(_ =&gt; sampleMethod()).Subscribe()。暂时忽略取消行为(这很容易添加),您只需将Interval 正确映射到表示为Task 的异步例程(内部视为IObservable&lt;&gt;,顺便说一句有.ToObservable() 扩展方法转换Task)。请注意,SelectMany(...) 在这里至关重要:否则 Rx 框架将不会等待 Task 完成,不会捕获可能的异常并且不会保留事件的原始顺序。

CancellationToken 用于取消订阅您编造的Observable。从这个意义上说,它类似于IDisposable,两者都服务于同一个目标:停止和清理您的订阅。因此,一旦你在上面调用Cancel(),你将不会得到任何更新。

附:你确定FromHours(1) 是对的吗?您可能需要更短的间隔,例如FromMinutre(1)

【讨论】:

  • 不明白,为什么这个方法不定期重复。我用 FromMinutre(1) 试过,但任务没有重复@Sereja Bogolubov
  • 我不同意其他答案。虽然 Rx 是一个外部库,但它是重复异步操作的更好概念。这不是过于复杂,而是简化,应该优先于任务!即使对于这个小用例
  • 您的代码无法编译。它需要是.SelectMany(_ =&gt; Observable.FromAsync(() =&gt; sampleMethod()))
  • @Enigmativity 您使用的是哪个 Rx 版本?
  • @SerejaBogolubov - 我正在开发它并获得 4.3.2 版本。
【解决方案4】:

如果您只需要计时器功能,则不需要 Rx。您可以使用来自System.ThreadingTimer

    var timer = new Timer(_ =>
    {
        // repeating code goes here
        // the argument is an optional state that you can pass into the constructor
    });
    timer.Change( dueTime: TimeSpan.FromSeconds(1), period: TimeSpan.FromSeconds(0.1));
    // run your app
    timer.Dispose();

Timer 的优点是它允许您随时重新配置它(如果您想更改周期,只需再次调用Change),暂时禁用它并干净地删除它(调用 Dispose)。

【讨论】:

    猜你喜欢
    • 2011-06-04
    • 1970-01-01
    • 1970-01-01
    • 2011-10-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-15
    相关资源
    最近更新 更多