我有一个Session
对象来管理对象状态(类似于nhibernate会话)。此会话侦听来自外部源的事件,该事件可能需要更新内部会话状态。我们已经尝试在会话中实现锁定,以确保访问的数据是连贯的,但是有很多边缘情况。
相反,将这些事件编组到与使用会话(即UI线程)相同的线程上可能更容易。通常,这是使用Control.Invoke()
完成的,但由于这是一个数据对象,因此无法访问Control
。
这是一种合理的方法吗?如何在更新会话状态之前将此事件发送到UI线程?我可以使用Dispatcher
并在创建会话时捕获当前线程的调度程序吗?
答案 0 :(得分:1)
我会让业务对象触发一个事件被视图(UI)捕获并对该事件处理程序进行编组,因此您可以在该位置使用Control
来了解是否需要调用或不:
public static class ControlExtentions
{
public delegate void InvokeHandler();
public static bool SafeInvoke(this Control control, InvokeHandler handler)
{
if (control.InvokeRequired)
{
try
{
control.Invoke(handler);
}
finally { }
return false;
}
else
handler.Invoke();
return true;
}
}
如果你使用WPF,你可以从CaliburnMicro获取灵感:
public static class Execute
{
private static Action<System.Action> executor = action => action();
/// <summary>
/// Initializes the framework using the current dispatcher.
/// </summary>
public static void InitializeWithDispatcher()
{
#if SILVERLIGHT
var dispatcher = Deployment.Current.Dispatcher;
executor = action => {
if(dispatcher.CheckAccess())
action();
else {
var waitHandle = new ManualResetEvent(false);
Exception exception = null;
dispatcher.BeginInvoke(() => {
try {
action();
}
catch(Exception ex) {
exception = ex;
}
waitHandle.Set();
});
waitHandle.WaitOne();
if(exception != null)
throw new TargetInvocationException("An error occurred while dispatching a call to the UI Thread", exception);
}
};
#else
var dispatcher = Dispatcher.CurrentDispatcher;
executor = action => {
if(dispatcher.CheckAccess())
action();
else dispatcher.Invoke(action);
};
#endif
}
/// <summary>
/// Resets the executor to use a non-dispatcher-based action executor.
/// </summary>
public static void ResetWithoutDispatcher() {
executor = action => action();
}
/// <summary>
/// Executes the action on the UI thread.
/// </summary>
/// <param name="action">The action to execute.</param>
public static void OnUIThread(this System.Action action) {
executor(action);
}
}
答案 1 :(得分:1)
我认为锁定通常非常简单,可能是您的最佳选择。正确实现你想要的东西可能要困难得多(当然也不是没有“繁琐的边缘情况”)。
你可以做的是使用.Net 4中的BlockingCollection<T>
的现有实现,并在单线程中从它中出列。不幸的是,该线程将被阻止,因此您无法使用UI线程。