C# 什么';使用Rx运行周期性任务的一个好方法是什么,只有一个并发执行限制?

C# 什么';使用Rx运行周期性任务的一个好方法是什么,只有一个并发执行限制?,c#,system.reactive,rx.net,C#,System.reactive,Rx.net,我希望在中运行周期性任务,限制在任何给定时间内最多只能运行一次方法执行 我正在试验Rx,但我不确定如何最多施加一次并发限制 var timer = Observable.Interval(TimeSpan.FromMilliseconds(100)); timer.Subscribe(tick => DoSomething()); 此外,如果任务仍在运行,则我希望后续的计划将过期。i、 e我不希望任务排队并导致问题 我有两个这样的任务要定期执行。当前正在执行的任务是同步的。但是,如果有

我希望在中运行周期性任务,限制在任何给定时间内最多只能运行一次方法执行

我正在试验Rx,但我不确定如何最多施加一次并发限制

var timer = Observable.Interval(TimeSpan.FromMilliseconds(100));
timer.Subscribe(tick => DoSomething());

此外,如果任务仍在运行,则我希望后续的计划将过期。i、 e我不希望任务排队并导致问题


我有两个这样的任务要定期执行。当前正在执行的任务是同步的。但是,如果有必要的话,我可以使它们异步。

如果你走的是正确的道路,你可以使用
选择
+
Concat
来平展可观察到的内容,并限制机上请求的数量(注意:如果你的任务花费的时间超过了间隔时间,那么它们将开始堆积,因为它们无法足够快地执行):


您应该按原样测试您的代码,因为这正是Rx已经实施的

尝试以下测试:

void Main()
{
    var timer = Observable.Interval(TimeSpan.FromMilliseconds(100));
    using (timer.Do(x => Console.WriteLine("!")).Subscribe(tick => DoSomething()))
    {
        Console.ReadLine();
    }
}

private void DoSomething()
{
    Console.Write("<");
    Console.Write(DateTime.Now.ToString("HH:mm:ss.fff"));
    Thread.Sleep(1000);
    Console.WriteLine(">");
}
void Main()
{
var定时器=可观察的时间间隔(TimeSpan.From毫秒(100));
使用(timer.Do(x=>Console.WriteLine(!)).Subscribe(tick=>DoSomething())
{
Console.ReadLine();
}
}
私人无效剂量()
{
控制台。写(“”);
}
运行此命令时,您将获得以下类型的输出:

!
<16:54:57.111>
!
<16:54:58.112>
!
<16:54:59.113>
!
<16:55:00.113>
!
<16:55:01.114>
!
<16:55:02.115>
!
<16:55:03.116>
!
<16:55:04.117>
!
<16:55:05.118>
!
<16:55:06.119
!
!
!
!
!
!
!
!
!
!

这里有一个工厂函数,它完全满足您的要求

public static IObservable<Unit> Periodic(TimeSpan timeSpan)
{
    return Observable.Return(Unit.Default).Concat(Observable.Return(Unit.Default).Delay(timeSpan).Repeat());
}
如果运行此命令,每个控制台打印的间隔大约为1.5秒

注意,如果您不想立即运行第一个勾号,您可以使用此工厂,它在时间跨度之后才会发送第一个单元

public static IObservable<Unit> DelayedPeriodic(TimeSpan timeSpan)
{
    return Observable.Return(Unit.Default).Delay(timeSpan).Repeat();
}
公共静态IObservable DelayedPeriodic(TimeSpan TimeSpan)
{
return Observable.return(Unit.Default).Delay(timeSpan.Repeat();
}

下面是
PeriodicSequentialExecution
方法的两个实现,该方法通过以周期性方式执行异步方法,强制执行无重叠执行策略来创建一个可观察的。后续执行之间的间隔可以延长,以防止重叠,在这种情况下,周期会相应地进行时间偏移

第一个实现纯粹是功能性的,而第二个实现主要是必需的。两种实现在功能上完全相同。第一个可以与自定义的
IScheduler
一起提供。第二种可能会稍微有效一些

功能实现:

/// <summary>
/// Creates an observable sequence containing the results of an asynchronous
/// action that is invoked periodically and sequentially (without overlapping).
/// </summary>
public static IObservable<T> PeriodicSequentialExecution<T>(
    Func<CancellationToken, Task<T>> action,
    TimeSpan dueTime, TimeSpan period,
    CancellationToken cancellationToken = default,
    IScheduler scheduler = null)
{
    // Arguments validation omitted
    scheduler ??= DefaultScheduler.Instance;
    return Delay(dueTime) // Initial delay
        .Concat(Observable.Using(() => CancellationTokenSource.CreateLinkedTokenSource(
            cancellationToken), linkedCTS => 
            // Execution loop
            Observable.Publish( // Start a hot delay timer before each operation
                Delay(period), hotTimer => Observable
                    .StartAsync(() => action(linkedCTS.Token)) // Start the operation
                    .Concat(hotTimer) // Await the delay timer
            )
            .Repeat()
            .Finally(() => linkedCTS.Cancel()) // Unsubscription: cancel the operation
        ));

    IObservable<T> Delay(TimeSpan delay)
        => Observable
            .Timer(delay, scheduler)
            .IgnoreElements()
            .Select(_ => default(T))
            .TakeUntil(Observable.Create<Unit>(o => cancellationToken.Register(() =>
                o.OnError(new OperationCanceledException(cancellationToken)))));
}
public static IObservable<T> PeriodicSequentialExecution2<T>(
    Func<CancellationToken, Task<T>> action,
    TimeSpan dueTime, TimeSpan period,
    CancellationToken cancellationToken = default)
{
    // Arguments validation omitted
    return Observable.Create<T>(async (observer, ct) =>
    {
        using (var linkedCTS = CancellationTokenSource.CreateLinkedTokenSource(
            ct, cancellationToken))
        {
            try
            {
                await Task.Delay(dueTime, linkedCTS.Token);
                while (true)
                {
                    var delayTask = Task.Delay(period, linkedCTS.Token);
                    var result = await action(linkedCTS.Token);
                    observer.OnNext(result);
                    await delayTask;
                }
            }
            catch (Exception ex) { observer.OnError(ex); }
        }
    });
}
//
///创建一个可观察序列,其中包含异步
///定期和顺序调用的操作(没有重叠)。
/// 
公共静态可观测周期SequentialExecution(
Func action,
TimeSpan dueTime,TimeSpan期间,
CancellationToken CancellationToken=默认值,
isScheduler调度程序(0=null)
{
//省略参数验证
调度器???=DefaultScheduler.Instance;
返回延迟(dueTime)//初始延迟
.Concat(可观察。使用(()=>CancellationTokenSource.CreateLinkedTokenSource(
cancellationToken),linkedCTS=>
//执行循环
Observable.Publish(//在每次操作之前启动热延迟计时器
延迟(周期),热定时器=>可观察
.StartAsync(()=>action(linkedCTS.Token))//启动操作
.Concat(hotTimer)//等待延迟计时器
)
.重复
.Finally(()=>linkedCTS.Cancel())//取消订阅:取消操作
));
IO可观测延迟(时间跨度延迟)
=>可观测
.计时器(延迟、调度程序)
.IgnoreElements()
.选择(=>default(T))
.TakeUntil(Observable.Create(o=>cancellationToken.Register)(()=>
o、 OnError(新操作取消异常(取消令牌()())));
}
当务之急是执行:

/// <summary>
/// Creates an observable sequence containing the results of an asynchronous
/// action that is invoked periodically and sequentially (without overlapping).
/// </summary>
public static IObservable<T> PeriodicSequentialExecution<T>(
    Func<CancellationToken, Task<T>> action,
    TimeSpan dueTime, TimeSpan period,
    CancellationToken cancellationToken = default,
    IScheduler scheduler = null)
{
    // Arguments validation omitted
    scheduler ??= DefaultScheduler.Instance;
    return Delay(dueTime) // Initial delay
        .Concat(Observable.Using(() => CancellationTokenSource.CreateLinkedTokenSource(
            cancellationToken), linkedCTS => 
            // Execution loop
            Observable.Publish( // Start a hot delay timer before each operation
                Delay(period), hotTimer => Observable
                    .StartAsync(() => action(linkedCTS.Token)) // Start the operation
                    .Concat(hotTimer) // Await the delay timer
            )
            .Repeat()
            .Finally(() => linkedCTS.Cancel()) // Unsubscription: cancel the operation
        ));

    IObservable<T> Delay(TimeSpan delay)
        => Observable
            .Timer(delay, scheduler)
            .IgnoreElements()
            .Select(_ => default(T))
            .TakeUntil(Observable.Create<Unit>(o => cancellationToken.Register(() =>
                o.OnError(new OperationCanceledException(cancellationToken)))));
}
public static IObservable<T> PeriodicSequentialExecution2<T>(
    Func<CancellationToken, Task<T>> action,
    TimeSpan dueTime, TimeSpan period,
    CancellationToken cancellationToken = default)
{
    // Arguments validation omitted
    return Observable.Create<T>(async (observer, ct) =>
    {
        using (var linkedCTS = CancellationTokenSource.CreateLinkedTokenSource(
            ct, cancellationToken))
        {
            try
            {
                await Task.Delay(dueTime, linkedCTS.Token);
                while (true)
                {
                    var delayTask = Task.Delay(period, linkedCTS.Token);
                    var result = await action(linkedCTS.Token);
                    observer.OnNext(result);
                    await delayTask;
                }
            }
            catch (Exception ex) { observer.OnError(ex); }
        }
    });
}
公共静态可观察周期SequentialExecution2(
Func action,
TimeSpan dueTime,TimeSpan期间,
CancellationToken CancellationToken=默认值)
{
//省略参数验证
返回可观察的。创建(异步(观察者,ct)=>
{
使用(var linkedCTS=CancellationTokenSource.CreateLinkedTokenSource(
ct,取消令牌)
{
尝试
{
等待任务延迟(dueTime,linkedCTS.Token);
while(true)
{
var delayTask=Task.Delay(period,linkedCTS.Token);
var result=等待操作(linkedCTS.Token);
OnNext观察员(结果);
等待任务的完成;
}
}
catch(Exception ex){observer.OnError(ex);}
}
});
}

cancellationToken
参数可用于生成的可观察序列的排序。这意味着序列在终止之前等待当前运行的操作完成。如果您希望它立即终止,可能会让工作以一种“火而忘”的方式运行而不被观察,那么您可以像往常一样简单地处理对可观察序列的订阅。取消
cancellationToken
会导致可观察序列在故障状态下完成(
OperationCanceledException
)。

只要
DoSomething
没有在后台启动任何操作,这是正确的。明白了。我对这个问题作了一些修改。我如何确保在任务时间超过间隔的情况下,任务不会排队。@smartnut007-同样,Rx会处理它。这就是我将
“!”
放在
.Do(…)
方法中的原因,也是我将间隔设置为
100
毫秒的原因。如果是排队的话,我得到的
“!”
的10倍,但我没有。是的