我遇到这种情况,我有两个属性,它们都通过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
的开头直接发送到硬件,所以如果我没有延迟,第二个参数会更改。
答案 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
}
}