即使在延迟内触发两次,执行方法也会延迟一次

时间:2015-09-15 06:19:45

标签: c#

我遇到这种情况,我有两个属性,它们都通过INotifyPropertyChanged进行了更改通知,并触发了我连接硬件的重新配置,这需要一些时间并且不能并行执行两次。

现在有一种情况是这两种属性都被改变了#34;同时#34;并将触发两个接一个的重新配置。

为了避免这种情况,我想在执行重新配置之前实现某种延迟,并且"忽略"第二个电话,如果一个已经等待。

是否有一些常用的模式或逻辑可以解决这样的问题? 目前我使用以下内容:

public class ExecutionManager
{
    private readonly object reconfigurationLock = new object();
    private bool reconfigurationScheduled;

    /// <summary>Reconfigures the measurement.</summary>
    public void ReconfigureMeasurement()
    {
        //slow/long method
    }

    /// <summary>Reconfigures the measurement delayed so two calls short after each other only trigger one reconfiguration.</summary>
    public void ReconfigureMeasurementDelayed()
    {
        lock (reconfigurationLock)
        {
            if (reconfigurationScheduled)
                return;
            reconfigurationScheduled = true;
        }

        Task.Run(async () =>
            {
                await Task.Delay(50).ConfigureAwait(false);
                ReconfigureMeasurement();
                reconfigurationScheduled = false;
            });
    }
}

......但这似乎有点&#34; hacky&#34;对我来说,我觉得有一种方法可以更好地解决我的问题。

要记住一件重要的事情:
在第二次更改活动之前我也不能打电话给ReconfigureMeasurement&#34;发生了#34;因为这会导致该值的更改值丢失,因为新值会在ReconfigureMeasurement的开头直接发送到硬件,所以如果我没有延迟,第二个参数会更改。

3 个答案:

答案 0 :(得分:1)

我认为你可以做到:

    /// <summary>Reconfigures the measurement delayed so two calls short after each other only trigger one reconfiguration.</summary>
public void ReconfigureMeasurementDelayed()
{
    lock (reconfigurationLock)
    {
        if (reconfigurationScheduled)
            return;
        reconfigurationScheduled = true;
    }

    Task.Run(() =>
        {                                
            ReconfigureMeasurement();
            lock (reconfigurationLock) {
                 reconfigurationScheduled = false;
            }
        });
}

无需任何延迟,无论如何都无法解决问题。

答案 1 :(得分:1)

INotifyPropertyChanged架构存在一个固有的弱点,因为它允许客户端观察处于不一致状态的模型。考虑下面的例子。请注意订阅者将如何收到有关值X更改的通知,然后收到有关值XSquared更改的通知。如果客户端类似于UI,当X发生变化时,它会返回并仅查看X,然后当XSquared发生变化时,它会返回并仅查看XSquared,然后问题就不会显现出来。但是,当您处理的情况是您需要查看模型的其他部分而不是立即通知时,您将面临看到无效状态的风险。

 class ViewModel : INotifyPropertyChanged
 {
     public int X
     {
         get { return _x; }
         set 
         {
             _x = value;
             _xSquared = _a * _a;
             NotifyPropertyChanged("X");
             NotifyPropertyChanged("XSquared");
         }
     }

     public int XSquared { get { return _xSquared; }
 }

长时间使用这些类型的系统后,我发现最好允许更改及其任何副作用完全解决,然后发送某些更改的单个通知。 e.g。

 class ViewModel
 {
     public event EventHandler SomethingChanged;

     public int X
     {
         get { return _x; }
         set 
         {
             _x = value;
             _xSquared = _a * _a;
             SomethingChanged(this, EventArgs.Empty);
         }
     }

     public int XSquared { get { return _xSquared; }
 }

 public class ExecutionManager
 {
     private ViewModel _viewModel;

     public void OnSomethingChanged(...)
     {
         // state is now consistent so long as everything is 
         // single threaded.
         ReconfigureHardware(_viewModel.X, _viewModel.XSquared);      
     }
 }

答案 2 :(得分:0)

目前我使用以下解决方案,感谢Arons评论:

public class ExecutionManager
{
    private readonly int reconfigurationDelay = 50;
    private IObservable<EventArgs> delayedReconfigurations;

    /// <summary>Occurs when a reconfiguration is required.</summary>
    public event EventHandler ReconfigurationRequired;
    /// <summary>Called when a reconfiguration is required.</summary>
    public void OnReconfigurationRequired() => ReconfigurationRequired.Raise(this);

    /// <summary>Initializes the manager.</summary>
    public override void Init()
    {
        base.Init();

        delayedReconfigurations = Observable.FromEvent<EventHandler, EventArgs>(
            h => (s, e) => h(e),
            h => ReconfigurationRequired += h,
            h => ReconfigurationRequired -= h);
        delayedReconfigurations.Throttle(TimeSpan.FromMilliseconds(reconfigurationDelay))
            .Subscribe(e => ReconfigureMeasurement());
    }

    /// <summary>Reconfigures the measurement.</summary>
    public void ReconfigureMeasurement()
    {
        //long/slow method
    }
}