C# 使用一个可观察对象作为时钟来测试另一个是否超时

C# 使用一个可观察对象作为时钟来测试另一个是否超时,c#,system.reactive,C#,System.reactive,我想做的事情的声明如下所示: // Checks input source for timeouts, based on the number of elements received // from clock since the last one received from source. // The two selectors are used to generate output elements. public static IObservable<R> Timeou

我想做的事情的声明如下所示:

// Checks input source for timeouts, based on the number of elements received 
// from clock since the last one received from source. 
// The two selectors are used to generate output elements.
public static IObservable<R> TimeoutDetector<T1,T2,R>(
        this IObservable<T1> source, 
        IObservable<T2> clock, 
        int countForTimeout,
        Func<R> timedOutSelector, 
        Func<T1, R> okSelector)
我试图寻找现有的
可观察的
函数,这些函数可以以我可以使用的方式组合
源代码
时钟
,但大多数组合函数依赖于接收“每个函数中的一个”(
Zip
),或者它们从“缺少的”函数中重新返回“上一个”值(
combinelatetest
),或者它们离我需要的太远了(
Amb
GroupJoin
Join
Merge
SelectMany
Timeout
)。
Sample
看起来很接近,但我不想将源吞吐量限制在时钟速率上

所以现在我一直在努力填补这个巨大的空白:

return new AnonymousObservable<R>(observer =>
{
    //One observer, two observables??
});
返回新的匿名可观察对象(observer=>
{
//一个观察者,两个观察者??
});
很抱歉,“您尝试了什么”部分在这里有点弱:假设我已经尝试过思考它!我不是要求全面实施,只是:

  • 是否有一个内置的功能可以帮助我,我错过了
  • 我如何构建一个基于lambda的观察者,它订阅两个观察者

我知道您没有要求全面实施,但我认为这是一个解决方案:

public static IObservable<TR> TimeoutDetector<T1, T2, TR>(
    this IObservable<T1> source,
    IObservable<T2> clock,
    int countForTimeout,
    Func<TR> timedOutSelector,
    Func<T1, TR> okSelector)
{
    return source
        .Select(i => clock.Take(countForTimeout).LastAsync())
        .Switch().Select(_ => timedOutSelector())
        .Merge(source.Select(okSelector));
}
尝试回答您的具体问题:

  • 有没有一个内置的功能可以帮助我,我错过了什么?答:可能扫描是关键
  • 问:我如何构建一个基于lambda的观察者,它订阅两个观察者?答:我不太清楚你的意思……有很多方法可以组合流,你提到了其中的大多数

我想出了这个,这比詹姆斯的答案更不漂亮

public static IObservable<R> TimeoutDetector2<T1, T2, R>(
        this IObservable<T1> source, 
        IObservable<T2> clock, int maxDiff,
        Func<R> timedOutSelector, Func<T1, R> okSelector)
{
    return new AnonymousObservable<R>(observer =>
    {
        int counter = 0;
        object gate = new object();
        bool error = false;
        bool completed = false;
        bool timedOut = false;
        var sourceSubscription = source.Subscribe(
            x =>
            {
                lock(gate)
                {
                    if(!error && !completed) observer.OnNext(okSelector(x));
                    counter = 0;
                    timedOut = false;
                }
            },
            ex =>
            {
                lock(gate)
                {
                    error = true;
                    if(!completed) observer.OnError(ex);
                }
            },
            () =>
            {
                lock(gate)
                {
                    completed = true;
                    if(!error) observer.OnCompleted();
                }
            });
        var clockSubscription = clock.Subscribe(
            x =>
            {
                lock(gate)
                {
                    counter = counter + 1;
                    if(!error && !completed && counter > maxDiff && !timedOut)
                    {
                        timedOut = true;
                        observer.OnNext(timedOutSelector());
                    }
                }
            },
            ex =>
            {
                lock(gate)
                {
                    error = true;
                    if(!completed) observer.OnError(ex);
                }
            },
            () =>
            {
                lock(gate)
                {
                    completed = true;
                    if(!error) observer.OnCompleted();
                }
            });

        //need to return a subscription
        return new CompositeDisposable(sourceSubscription, clockSubscription);
    }).Publish().RefCount(); // prevent subscribers provoking more than one subscription to source and clock
}
公共静态IObservable TimeoutDetector 2(
这是一个可观测的来源,
IObservable时钟,int maxDiff,
Func timedOutSelector,Func okSelector)
{
返回新的匿名观察者(观察者=>
{
int计数器=0;
对象门=新对象();
布尔误差=假;
bool completed=false;
bool-timedOut=false;
var sourceSubscription=source.subscription(
x=>
{
锁(门)
{
如果(!error&&!completed)observer.OnNext(okSelector(x));
计数器=0;
timedOut=false;
}
},
ex=>
{
锁(门)
{
错误=真;
如果(!完成)观察者错误(ex);
}
},
() =>
{
锁(门)
{
完成=正确;
如果(!error)observer.OnCompleted();
}
});
var clockSubscription=clock.subscription(
x=>
{
锁(门)
{
计数器=计数器+1;
如果(!error&&!completed&&counter>maxDiff&&!timedOut)
{
timedOut=true;
OnNext(timedOutSelector());
}
}
},
ex=>
{
锁(门)
{
错误=真;
如果(!完成)观察者错误(ex);
}
},
() =>
{
锁(门)
{
完成=正确;
如果(!error)observer.OnCompleted();
}
});
//需要返回订阅
返回新的CompositeDisposable(sourceSubscription、clockSubscription);
}).Publish().RefCount();//防止订阅服务器引发对源和时钟的多个订阅
}

以下是我提到的可观察的创建方法(相同的测试工作):

公共静态IObservable超时检测器(
这是一个可观测的来源,
可观测时钟,
int countForTimeout,
Func timedOutSelector,
Func(选择器)
{
返回可观察的。创建(观察者=>
{
var计数器=countForTimeout;
var timeoutSub=clock.Subscribe(=>
{
var计数=联锁减量(参考计数器);
如果(计数=0)
{
OnNext(timedOutSelector());
}
},
observer.OnError,
观察员(未完成);
var sourceSub=source.Subscribe(
i=>
{
联锁交换(参考计数器,countForTimeout);
OnNext观察员(okSelector(i));
},
observer.OnError,
观察员(未完成);
返回新的CompositeDisposable(sourceSub、timeoutSub);
});
}

请注意,Observable.Create对于确保使用正确的Rx语法非常有用(即流发出OnNext*(OnError | OnCompleted)-这意味着我可以轻松地发送一个错误或一次完成。

当然这是一个老问题。我一直在寻找比
timeout更高级的东西,以便取消超时逻辑

我想知道超时是否相当于:

race(throwError('timedout').pipe(delay(10000)), yourObs$)
当然,这里显示的“投掷者”是可以取消的

如果你想知道为什么-我有一些由可观察链控制的“步骤”,我有一个超时。但是如果其中一个步骤包括打开一个对话框,那么我希望取消超时!

我恨你:)我会想出一些稍微多一点的…冗长的(这也说明了我订阅两个可观察对象的意思).现在我得走了
public static IObservable<R> TimeoutDetector2<T1, T2, R>(
        this IObservable<T1> source, 
        IObservable<T2> clock, int maxDiff,
        Func<R> timedOutSelector, Func<T1, R> okSelector)
{
    return new AnonymousObservable<R>(observer =>
    {
        int counter = 0;
        object gate = new object();
        bool error = false;
        bool completed = false;
        bool timedOut = false;
        var sourceSubscription = source.Subscribe(
            x =>
            {
                lock(gate)
                {
                    if(!error && !completed) observer.OnNext(okSelector(x));
                    counter = 0;
                    timedOut = false;
                }
            },
            ex =>
            {
                lock(gate)
                {
                    error = true;
                    if(!completed) observer.OnError(ex);
                }
            },
            () =>
            {
                lock(gate)
                {
                    completed = true;
                    if(!error) observer.OnCompleted();
                }
            });
        var clockSubscription = clock.Subscribe(
            x =>
            {
                lock(gate)
                {
                    counter = counter + 1;
                    if(!error && !completed && counter > maxDiff && !timedOut)
                    {
                        timedOut = true;
                        observer.OnNext(timedOutSelector());
                    }
                }
            },
            ex =>
            {
                lock(gate)
                {
                    error = true;
                    if(!completed) observer.OnError(ex);
                }
            },
            () =>
            {
                lock(gate)
                {
                    completed = true;
                    if(!error) observer.OnCompleted();
                }
            });

        //need to return a subscription
        return new CompositeDisposable(sourceSubscription, clockSubscription);
    }).Publish().RefCount(); // prevent subscribers provoking more than one subscription to source and clock
}
public static IObservable<TR> TimeoutDetector<T1, T2, TR>(
    this IObservable<T1> source,
    IObservable<T2> clock,
    int countForTimeout,
    Func<TR> timedOutSelector,
    Func<T1, TR> okSelector)
{
    return Observable.Create<TR>(observer =>
        {
            var counter = countForTimeout;

            var timeoutSub = clock.Subscribe(_ =>
                {
                    var count = Interlocked.Decrement(ref counter);
                    if (count == 0)
                    {
                        observer.OnNext(timedOutSelector());
                    }
                },
                observer.OnError,
                observer.OnCompleted);

            var sourceSub = source.Subscribe(
                i =>
                {
                    Interlocked.Exchange(ref counter, countForTimeout);
                    observer.OnNext(okSelector(i));
                },
                observer.OnError,
                observer.OnCompleted);

            return new CompositeDisposable(sourceSub, timeoutSub);
        });
}
race(throwError('timedout').pipe(delay(10000)), yourObs$)