C#Winforms线程:调用封闭形式

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

下面的代码演示了我的困境。代码创建一个后台线程来处理某些内容,然后用结果调用UI线程

如果后台线程在窗体关闭后调用窗体上的Invoke,它可能会引发异常。它在调用Invoke之前检查IsHandleCreated,但检查后表单可能会关闭

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