Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/mercurial/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 稳定后将属性值发送到服务器_C#_Wpf_Asynchronous_Client Server - Fatal编程技术网

C# 稳定后将属性值发送到服务器

C# 稳定后将属性值发送到服务器,c#,wpf,asynchronous,client-server,C#,Wpf,Asynchronous,Client Server,我有一些代码如下所示: class MyVM : VMBase { public MyVM(IMyServerProxy proxy) { _proxy = proxy; _proxy.ValueChanged += OnValueChangedFromServer; } private void OnValueChangedFromServer(int value){ _value = value; RaisePropertyChanged(() =

我有一些代码如下所示:

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
    }
  }
}
问题是:值绑定到滑块控件。那个滑块触发了很多变化。我不想把所有这些都发送到服务器。我只想发送稳定值。本质上,我想在值设置器中插入一些东西,该设置器仅在值一整秒钟未更改后调用代理。(我的第二个担忧是服务器会将过期的值更改路由回我,但我认为如果我只是延迟发送到服务器,那么这一问题将得到很大缓解。)


我研究了使用
Task.Delay
方法。然而,如果我取消延迟,它会抛出一个异常,并且在每次更新时构造一个新的
CancellationSource
,似乎也不理想。有更好的方法吗?

这种技术属于称为“事件合并”的逻辑范畴。占用空间最小的实现可能如下所示:

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 Value="{Binding Value, Delay=1000}"

在考虑了包括Rx扩展在内的各种选项后,我选择了
System.Threading.Timer
类。当您调用类的
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();
}
二传手:

_value = value;
_sendingValue = true;
_valueUpdater.Change(DelayMs, 0);
析构函数:

private bool _isDisposed;
public void Dispose()
{
    _isDisposed = true;

    if (!_sendingValue)
        _valueUpdater.Dispose();
...

我认为这在我的场景中不起作用,因为我有几个其他文本字段绑定到该值。我希望它们都随着滑块的移动而更新。我可以将它们绑定到滑块控件的值,而不是视图模型的值,但是我的可信度很低;绑定到绑定失败了很多次。@EugenePodskal,有了你的建议,第一次更改总是会被发送出去。这也意味着某些属性正在定期设置,而事实并非如此。我认为这行不通。