我正在尝试模拟时钟的行为。为此,我创建了一个Observable.Interval
,在指定的时间间隔之后,用相等的时间量更新属性的值。该值与GUI绑定数据。
问题是:"时钟"运行速度比预期慢,也就是说,时钟值增加一秒需要超过一秒的时间。
所需的时间分辨率越精细(下面的MILLIS_INTERVAL
的值),问题就越严重(我测试的值为1,10,100和1000的其他约数,因为TimeSpan.FromMilliseconds
记录的最大分辨率为1毫秒。
我希望可观察的回调是异步运行的,并且可以定期触发,可能是并行的,尽管执行时间很长,但似乎频率越大,滞后越多。
视图模型
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>
答案 0 :(得分:1)
计时器似乎在同一个线程上调用OnNext
,因此回调不会被执行&#34;异步&#34;。
您可以创建自己的方法来调用线程池线程上的回调。以下是使用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);
这应该使&#34;回调以异步方式运行,并且可以定期触发,可能是并行触发,尽管需要执行时间,...&#34;。
答案 1 :(得分:1)
Observable.Interval
运算符计算运行每个.OnNext
之间的时间间隔。因此,如果.OnNext
取0.1秒而你的间隔为1.0秒,那么你的有效期为1.1秒。但当然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%的误差,但结果是相同的 - 你有一个非常精确的“秒”值从这个可观测量返回,不是每一秒都准确回归。