稳定后向服务器发送 属性 值
send property value to server after stabilization
我有一些代码如下所示:
class MyVM : VMBase {
public MyVM(IMyServerProxy proxy) {
_proxy = proxy;
_proxy.ValueChanged += OnValueChangedFromServer;
}
private void OnValueChangedFromServer(int value){
_value = value;
RaisePropertyChanged(() => Value);
}
public int Value { // bound to slider
get { return _value; }
set {
_value = value;
// need something here to only send stable values to server
_proxy.ModifyValue(value); // async
}
}
}
问题是这样的:值绑定到滑块控件。该滑块触发了很多变化。我不想将所有这些发送到服务器。我只想发送稳定值。本质上,我想在 Value setter 中插入一些东西,它只在 Value 整整一秒没有改变后才调用代理。 (我有一个次要的担心,即服务器会将过时的值更改路由回给我,但我认为如果我只是延迟发送到服务器,这将在很大程度上得到缓解。)
我研究了使用 Task.Delay
方法。但是,如果我取消延迟,它会抛出异常,并且在每次更新时构建一个新的 CancellationSource
似乎也不理想。有没有更好的方法?
此技术属于称为 "event coalescing" 的逻辑类别。可能占用空间最少的实现如下:
class MyVM : VMBase {
private bool _isChangePending = false;
public MyVM(IMyServerProxy proxy) {
_proxy = proxy;
_proxy.ValueChanged += OnValueChangedFromServer;
}
private void OnValueChangedFromServer(int value){
_value = value;
RaisePropertyChanged(() => Value);
}
public int Value { // bound to slider
get { return _value; }
set {
lock(_isChangePending) {
_value = value;
// only send send "stable" values to server
if (!_isChangePending){
_isChangePending = true;
System.Threading.ThreadPool.QueueUserWorkItem(delegate {
this.SendAfterStabilize(value);
}, null);
}
}
}
}
private void SendAfterStabilize(int lastChangedValue) {
while (true) {
System.Threading.Thread.Sleep(1000); // control coalescing delay here
lock(_isChangePending) {
if (_value == lastChangedValue) {
_isChangePending = false;
_proxy.ModifyValue(lastChangedValue); // async
return;
}
else {
lastChangedValue = _value;
}
}
}
}
}
请注意,lock() { }
块在技术上是必要的,以保证每次可能的最后一次更改(无论时间如何)总是在一秒后到达服务器。如果删除 lock() { }
块,代码在 99.99% 的时间仍然可以工作,但在极少数情况下,最后所做的更改可能永远不会发送到服务器(由于线程之间缺乏内存访问同步) .
在 .NET Framework 4.5 或更高版本中,您可以在 Slider
控件中使用 BindingBase.Delay Property:
<Slider Value="{Binding Value, Delay=1000}"
在考虑了各种选项(包括 Rx 扩展)之后,我选择了 System.Threading.Timer
class。 class 在您调用其 Change
方法时重新启动其计时器。
字段:
private readonly System.Threading.Timer _valueUpdater;
private bool _sendingValue;
在构造函数中:
_valueUpdater = new System.Threading.Timer(OnSendValue, null, System.Threading.Timeout.Infinite, 0);
回调:
private void OnSendValue(object state)
{
_proxy.ModifyValue(_value).Wait();
_sendingValue = false;
if (_isDisposed)
_valueUpdater.Dispose();
}
Setter:
_value = value;
_sendingValue = true;
_valueUpdater.Change(DelayMs, 0);
析构函数:
private bool _isDisposed;
public void Dispose()
{
_isDisposed = true;
if (!_sendingValue)
_valueUpdater.Dispose();
...
我有一些代码如下所示:
class MyVM : VMBase {
public MyVM(IMyServerProxy proxy) {
_proxy = proxy;
_proxy.ValueChanged += OnValueChangedFromServer;
}
private void OnValueChangedFromServer(int value){
_value = value;
RaisePropertyChanged(() => Value);
}
public int Value { // bound to slider
get { return _value; }
set {
_value = value;
// need something here to only send stable values to server
_proxy.ModifyValue(value); // async
}
}
}
问题是这样的:值绑定到滑块控件。该滑块触发了很多变化。我不想将所有这些发送到服务器。我只想发送稳定值。本质上,我想在 Value setter 中插入一些东西,它只在 Value 整整一秒没有改变后才调用代理。 (我有一个次要的担心,即服务器会将过时的值更改路由回给我,但我认为如果我只是延迟发送到服务器,这将在很大程度上得到缓解。)
我研究了使用 Task.Delay
方法。但是,如果我取消延迟,它会抛出异常,并且在每次更新时构建一个新的 CancellationSource
似乎也不理想。有没有更好的方法?
此技术属于称为 "event coalescing" 的逻辑类别。可能占用空间最少的实现如下:
class MyVM : VMBase {
private bool _isChangePending = false;
public MyVM(IMyServerProxy proxy) {
_proxy = proxy;
_proxy.ValueChanged += OnValueChangedFromServer;
}
private void OnValueChangedFromServer(int value){
_value = value;
RaisePropertyChanged(() => Value);
}
public int Value { // bound to slider
get { return _value; }
set {
lock(_isChangePending) {
_value = value;
// only send send "stable" values to server
if (!_isChangePending){
_isChangePending = true;
System.Threading.ThreadPool.QueueUserWorkItem(delegate {
this.SendAfterStabilize(value);
}, null);
}
}
}
}
private void SendAfterStabilize(int lastChangedValue) {
while (true) {
System.Threading.Thread.Sleep(1000); // control coalescing delay here
lock(_isChangePending) {
if (_value == lastChangedValue) {
_isChangePending = false;
_proxy.ModifyValue(lastChangedValue); // async
return;
}
else {
lastChangedValue = _value;
}
}
}
}
}
请注意,lock() { }
块在技术上是必要的,以保证每次可能的最后一次更改(无论时间如何)总是在一秒后到达服务器。如果删除 lock() { }
块,代码在 99.99% 的时间仍然可以工作,但在极少数情况下,最后所做的更改可能永远不会发送到服务器(由于线程之间缺乏内存访问同步) .
在 .NET Framework 4.5 或更高版本中,您可以在 Slider
控件中使用 BindingBase.Delay Property:
<Slider Value="{Binding Value, Delay=1000}"
在考虑了各种选项(包括 Rx 扩展)之后,我选择了 System.Threading.Timer
class。 class 在您调用其 Change
方法时重新启动其计时器。
字段:
private readonly System.Threading.Timer _valueUpdater;
private bool _sendingValue;
在构造函数中:
_valueUpdater = new System.Threading.Timer(OnSendValue, null, System.Threading.Timeout.Infinite, 0);
回调:
private void OnSendValue(object state)
{
_proxy.ModifyValue(_value).Wait();
_sendingValue = false;
if (_isDisposed)
_valueUpdater.Dispose();
}
Setter:
_value = value;
_sendingValue = true;
_valueUpdater.Change(DelayMs, 0);
析构函数:
private bool _isDisposed;
public void Dispose()
{
_isDisposed = true;
if (!_sendingValue)
_valueUpdater.Dispose();
...