将Rx聚合保存到本地变量

时间:2014-12-07 01:52:01

标签: c# system.reactive

我正在尝试收集有关IObservable延迟流的统计信息。就我而言,这些流来自ETW跟踪文件的Tx驱动程序。

我已经编写了窗口聚合,每秒都会给我这些统计信息,我遇到了“完整跟踪”统计信息的问题,当流接收OnComplete时,我希望这些统计信息能够返回。以下是说明“完整跟踪”统计信息查询问题的代码。

该流有两种类型的延迟,很容易区分,任何> 10是一种延迟,< 10是另一种类型。

struct Duration
{
    DateTime OccurenceTime;
    double Latency;
}

IObservable<Duration> observableDurations = ...

var statistics = 
    from duration in observableDurations
    group duration by duration.Latency > 10 into g
    select new
    {
        Type = g.Key ? "Latency Type A" : "Latency Type B",
        Min  = g.Min(o => o.Latency),
        Max  = g.Max(o => o.Latency),
        Avg  = g.Average(o => o.Latency),
    };
// statistics.Dump(); In LinqPad this shows correct results.

double maxLatencyTypeA = 0;
statistics.Subscribe(x =>
{
    if (x.Type == "Latency Type A")
    {
        maxLatencyTypeA = x.Max;
        // I would also save min and average into local variables
    }
    else
    {
        // I would also save the statistics for TypeB latencies into local variables
    }
});

这不能编译,因为在统计查询的定义中,g.Min,g.Max和g.Average是IObservable<double>,但在lambda表达式中我传递给statistics.Suscribe() 我尝试将g.Max保存到显然与double不兼容的IObservable<double>

我可以通过将maxLatencyTypeA = x.Max;更改为x.Max.Subscribe(s=>maxLatencyTypeA=s);来解决这个问题。我的问题是,有没有办法编写一个更好的统计查询,为统计数据生成double而不是IObservable<double>的类型,这样我就不需要在统计订阅中嵌入更多订阅我可能想保存的每个统计数据?

由于

2 个答案:

答案 0 :(得分:2)

不要使用单独的运算符进行单独聚合,而是使用Aggregate并立即收集所有聚合。这样你就可以拥有一个包含所有结果的observable。注意我的计算平均值的算法由于浮点数学而受到一点漂移的影响。如果这是一个问题,那么你也可以保持一个运行的Sum并与Count一起使用来计算平均值。

var statistics = observableDurations
    .GroupBy(d => d.Latency > 10)
    .Select(g =>
    {
        var seed = new
        {
            Type = g.Key ? "A" : "B",
            Min = Double.MaxValue,
            Max = Double.MinValue,
            Average = 0.0,
            Count = 0
        };
        return g.Aggregate(seed, (total, duration) => new
        {
            Type = total.Type,
            Min = Math.Min(total.Min, duration.Latency),
            Max = Math.Max(total.Max, duration.Latency),
            Average = (total.Average * total.Count + duration.Latency) / (total.Count + 1),
            Count = total.Count + 1
        });
    });

答案 1 :(得分:1)

以下是两种不同的方式:

(两者未经测试)

statistics.Select(a => 
     a.Min.Zip(a.Max, a.Avg, (min, max, avg) => new { a.Type, min, max, avg }))
          .Merge();

statistics.Select(a => new
           {
             a.Type, 
             Min = a.Min.ToTask(),
             Max = a.Max.ToTask(),
             Avg = a.Avg.ToTask()
           })
           .Subscribe(async x =>
           {
             var min = await x.Min;
             var max = await x.Max;
             var avg = await x.Avg;
             ...
           });