[英]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.