C#Winforms线程:调用封闭形式
下面的代码演示了我的困境。代码创建一个后台线程来处理某些内容,然后用结果调用UI线程 如果后台线程在窗体关闭后调用窗体上的Invoke,它可能会引发异常。它在调用Invoke之前检查IsHandleCreated,但检查后表单可能会关闭C#Winforms线程:调用封闭形式,c#,winforms,thread-safety,invoke,C#,Winforms,Thread Safety,Invoke,下面的代码演示了我的困境。代码创建一个后台线程来处理某些内容,然后用结果调用UI线程 如果后台线程在窗体关闭后调用窗体上的Invoke,它可能会引发异常。它在调用Invoke之前检查IsHandleCreated,但检查后表单可能会关闭 void MyMethod() { // Define background thread Action action = new Action( () => { // Process
void MyMethod()
{
// Define background thread
Action action = new Action(
() =>
{
// Process something
var data = BackgroundProcess();
// Try to ensure the form still exists and hope
// that doesn't change before Invoke is called
if (!IsHandleCreated)
return;
// Send data to UI thread for processing
Invoke(new MethodInvoker(
() =>
{
UpdateUI(data);
}));
});
// Queue background thread for execution
action.BeginInvoke();
}
一种解决方案可能是同步FormClosing和要调用的每个调用,但这听起来不是很优雅。有更简单的方法吗?在调用窗体之前,可以检查窗体(或任何控件)上的IsDisposed 您还应该在正在调用的实际方法中检查这一点,以防表单在此期间被释放。看一看。
Post
方法在UI线程上发布对UpdateUI
委托的调用,而不需要专用窗口;这使您可以跳过调用IsHandleCreated
和Invoke
编辑:MSDN在下面有一些代码示例
您可能会发现通过AsyncOperationManager
类编程更容易,该类位于WindowsFormsSynchronizationContext
之上。反过来,BackgroundWorker
组件构建在AsyncOperationManager
之上
UI线程被定义为调用
AsyncOperationManager.CreateOperation
的线程;当您知道自己在UI线程上时,您希望在MyMethod
的开始处调用CreateOperation
,并在局部变量中捕获其返回值。是的,这里存在竞争。A在目标开始运行之前需要很好的毫秒。如果改用Control.BeginInvoke(),则效果会更好,表单的Dispose()实现将清空调度队列。但这仍然是一场竞赛,尽管很少有罢工。代码段中编写的代码不需要Invoke()
唯一干净的修复方法是联锁FormClosing事件,并延迟关闭,直到确认后台线程已完成且无法再次启动。按原样处理代码并不容易,因为这需要一个“已完成”回调,这样才能真正关闭表单。Q&D解决方案是捕获BeginInvoke将引发的ObjectDisposedException。考虑到使用BeginInvoke()时这种情况是多么罕见,这种丑陋的黑客行为是可以接受的。您无法测试它:)我通过使用Hans Passant的建议捕获ObjectDisposedException解决了BeginInvoke的同步问题。到目前为止,它似乎奏效了。我创建了控件类的扩展方法来实现这一点 TryBeginInvoke尝试在控件上调用自己的方法。如果成功调用该方法,它将检查是否已释放该控件。如果已处理,则立即返回;否则,它将调用最初作为参数传递给TryBeginInvoke的方法。代码如下:
public static class ControlExtension
{
// --- Static Fields ---
static bool _fieldsInitialized = false;
static InvokeDelegateDelegate _methodInvokeDelegate; // Initialized lazily to reduce application startup overhead [see method: InitStaticFields]
static InvokeMethodDelegate _methodInvokeMethod; // Initialized lazily to reduce application startup overhead [see method: InitStaticFields]
// --- Public Static Methods ---
public static bool TryBeginInvoke(this Control control, Delegate method, params object[] args)
{
IAsyncResult asyncResult;
return TryBeginInvoke(control, method, out asyncResult, args);
}
/// <remarks>May return true even if the target of the invocation cannot execute due to being disposed during invocation.</remarks>
public static bool TryBeginInvoke(this Control control, Delegate method, out IAsyncResult asyncResult, params object[] args)
{
if (!_fieldsInitialized)
InitStaticFields();
asyncResult = null;
if (!control.IsHandleCreated || control.IsDisposed)
return false;
try
{
control.BeginInvoke(_methodInvokeDelegate, control, method, args);
}
catch (ObjectDisposedException)
{
return false;
}
catch (InvalidOperationException) // Handle not created
{
return false;
}
return true;
}
public static bool TryBeginInvoke(this Control control, MethodInvoker method)
{
IAsyncResult asyncResult;
return TryBeginInvoke(control, method, out asyncResult);
}
/// <remarks>May return true even if the target of the invocation cannot execute due to being disposed during invocation.</remarks>
public static bool TryBeginInvoke(this Control control, MethodInvoker method, out IAsyncResult asyncResult)
{
if (!_fieldsInitialized)
InitStaticFields();
asyncResult = null;
if (!control.IsHandleCreated || control.IsDisposed)
return false;
try
{
control.BeginInvoke(_methodInvokeMethod, control, method);
}
catch (ObjectDisposedException)
{
return false;
}
catch (InvalidOperationException) // Handle not created
{
return false;
}
return true;
}
// --- Private Static Methods ---
private static void InitStaticFields()
{
_methodInvokeDelegate = new InvokeDelegateDelegate(InvokeDelegate);
_methodInvokeMethod = new InvokeMethodDelegate(InvokeMethod);
}
private static object InvokeDelegate(Control control, Delegate method, object[] args)
{
if (!control.IsHandleCreated || control.IsDisposed)
return null;
return method.DynamicInvoke(args);
}
private static void InvokeMethod(Control control, MethodInvoker method)
{
if (!control.IsHandleCreated || control.IsDisposed)
return;
method();
}
// --- Private Nested Types ---
delegate object InvokeDelegateDelegate(Control control, Delegate method, object[] args);
delegate void InvokeMethodDelegate(Control control, MethodInvoker method);
}
公共静态类ControlExtension
{
//---静态场---
静态布尔_fieldsInitialized=false;
static invokedelegate _methodInvokeDelegate;//延迟初始化以减少应用程序启动开销[请参阅方法:InitStaticFields]
静态InvokeMethodElegate _methodInvokeMethod;//延迟初始化以减少应用程序启动开销[请参阅方法:InitStaticFields]
//---公共静态方法---
公共静态bool TryBeginInvoke(此控件、委托方法、参数对象[]args)
{
IAsyncResult异步结果;
返回TryBeginInvoke(控件、方法、输出异步结果、参数);
}
///即使调用的目标由于在调用期间被释放而无法执行,也可能返回true。
公共静态bool TryBeginInvoke(此控件、委托方法、输出IAsyncResult asyncResult、参数对象[]args)
{
如果(!\u字段已中国化)
InitStaticFields();
asyncResult=null;
如果(!control.IsHandleCreated | | control.IsDisposed)
返回false;
尝试
{
control.BeginInvoke(_methodInvokeDelegate,control,method,args);
}
捕获(ObjectDisposedException)
{
返回false;
}
catch(InvalidOperationException)//未创建句柄
{
返回false;
}
返回true;
}
公共静态bool TryBeginInvoke(此控件,MethodInvoker方法)
{
IAsyncResult异步结果;
返回TryBeginInvoke(控件、方法、输出异步结果);
}
///即使调用的目标由于在调用期间被释放而无法执行,也可能返回true。
公共静态bool TryBeginInvoke(此控件、MethodInvoker方法、out IAsyncResult asyncResult)
{
如果(!\u字段已中国化)
InitStaticFields();
asyncResult=null;
如果(!control.IsHandleCreated | | control.IsDisposed)
返回false;
尝试
{
control.BeginInvoke(_methodInvokeMethod,control,method);
}
捕获(ObjectDisposedException)
{
返回false;
}
catch(InvalidOperationException)//未创建句柄
{
返回false;
}
返回true;
}
//---私有静态方法---
私有静态void InitStaticFields()
{
_methodInvokeDelegate=新的InvokeDelegate(InvokeDelegate);
_methodInvokeMethod=新的InvokeMethodElegate(InvokeMethod);
}
私有静态对象InvokeDelegate(控件控件、委托方法、对象[]args)
{
如果(!control.IsHandleCreated | | control.IsDisposed)
返回null;
返回方法DynamicInvoke(args);
}
私有静态void InvokeMethod(控件控件,MethodInvoker方法)
{
如果(!control.IsHandleCreated | | control.IsDis