繁体   English   中英

Observable.Interval未以预期的频率更新UI

[英]Observable.Interval not updating UI with expected frequency

我正在尝试模拟时钟的行为。 为此,我创建了一个Observable.Interval ,它在指定的时间间隔后,以相等的时间量更新属性的值。 该值与GUI数据绑定。

问题是:“时钟”运行得比预期的慢得多,也就是说,时钟值增加一秒需要花费一秒以上的时间。

所需的时间分辨率越精细(下面的MILLIS_INTERVAL的值),问题MILLIS_INTERVAL严重(我使用值MILLIS_INTERVAL和1000的其他约数进行了测试,因为据记录TimeSpan.FromMilliseconds的最大分辨率为1ms )。

我希望可观察到的回调能够异步运行,并且尽管有一定的执行时间,但可以定期触发,甚至可能并行触发,但似乎频率越大,滞后就越多。

视图模型

using System;
using System.ComponentModel;
using System.Reactive.Linq;

namespace IntervalClock
{
    public class ViewModel : INotifyPropertyChanged
    {

        public double TimeSeconds
        {
            get { return _timeSeconds; }
            set
            {
                _timeSeconds = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("TimeSeconds"));
            }
        }
        double _timeSeconds;


        const double MILLIS_INTERVAL = 10;

        public ViewModel()
        {
            Observable.Interval(TimeSpan.FromMilliseconds(MILLIS_INTERVAL))
                      .Subscribe(token => TimeSeconds += MILLIS_INTERVAL / 1000);
        }


        public event PropertyChangedEventHandler PropertyChanged;
    }
}

主窗口:

<Window x:Class="IntervalClock.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:IntervalClock"
        mc:Ignorable="d">

    <Window.DataContext>
        <local:ViewModel/>
    </Window.DataContext>

    <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
        <TextBlock Text="{Binding TimeSeconds, StringFormat=N3}" FontSize="30"/>
    </StackPanel>
</Window>

计时器似乎在同一线程上调用OnNext ,因此不会“异步”执行回调。

您可以创建自己的方法来调用线程池线程上的回调。 这是使用System.Timers.Timer类的基本示例:

public static IObservable<long> Interval(TimeSpan interval)
{
    return Observable.Create<long>(observer =>
    {
        long i = 0;
        Timer _timer = new Timer(interval.TotalMilliseconds);
        _timer.Elapsed += (s, e) => observer.OnNext(i++);
        _timer.Start();

        return Disposable.Create(() =>
        {
            _timer.Stop();
            _timer.Dispose();
        });
    });
}

用法(只需调用您自己的Interval方法而不是Observable.Interval ):

Interval(TimeSpan.FromMilliseconds(MILLIS_INTERVAL))
                  .Subscribe(token => TimeSeconds += MILLIS_INTERVAL / 1000);

这应该使“回调异步运行,并且尽管有一定的执行时间,但仍要以规则的时间间隔(可能是并行触发)触发”。

Observable.Interval运算符对运行每个.OnNext触发之间的间隔进行.OnNext 因此,如果.OnNext花费0.1s且间隔为1.0s,则有效期为1.1s。 但是,当然Windows不是实时操作系统,因此它具有漂移性。 您的实际时间可能会更大,尤其是在系统负载时。

这是我的处理方式:

var interval = TimeSpan.FromSeconds(1.0);
Observable
    .Create<long>(o =>
    {
        var sw = Stopwatch.StartNew();
        return
            Observable
                .Timer(TimeSpan.Zero, TimeSpan.FromSeconds(interval.TotalSeconds / 10.0))
                .Select(x => (long)sw.Elapsed.TotalSeconds)
                .DistinctUntilChanged()
                .Subscribe(o);
    })

StopWatch的使用为您提供了出色的经过时间精度。 计时器的触发频率比interval触发的频率大约高10倍,因此在触发时会出现+/- 10%的错误,但结果是相同的-从此可观察值返回的是非常准确的“秒”值,只是不返回精确地每秒

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM