C#:如何防止Invoke失败

时间:2009-09-07 18:49:21

标签: c# winforms multithreading event-handling

我有一个UserControl,可以完成一些后台工作。当事物有一个已完成的事件,用户控件订阅了该事件。由于事件是在一个单独的线程中引发的,并且事件处理程序对用户控件进行了一些更新,因此我使用以下命令启动事件处理程序:

private void ProcessProcessCompleted(object sender, ProcessCompletedEventArgs e)
{
    if (InvokeRequired)
    {
        Invoke(new Action<object, ProcessCompletedEventArgs>(ProcessProcessCompleted), 
            new[] { sender, e });
        return;
    }

    // Update the user control, etc.
}

这样可以正常工作,除非在进程完成之前关闭包含用户控件的表单。我试图取消订阅用户控件的dispose方法中的事件,但这似乎只是工作的一部分时间。我该怎么做?想避免将它包装在try catch块中。在我调用之前是否有InvokeIsPossible属性或我可以查看的内容?

3 个答案:

答案 0 :(得分:3)

您可以使用SynchronizationContext,它是在.NET 2中引入的,以简化线程间通信:

  • 创建一个全局SynchronizationContext变量(比如说syncContext)
  • 将其初始化为表单的thread sync'context:

    syncContext = SynchronizationContext.Current;

像这样更改ProcessProcessCompleted方法:

private void ProcessProcessCompleted(object sender, object e)
{
    this.syncContext.Post(new SendOrPostCallback(delegate(object state)
    {
        // Update the user control, etc.
    })
    , null);            
} 

您可以在此处阅读SynchronizationContext

http://www.codeproject.com/KB/cpp/SyncContextTutorial.aspx

http://msdn.microsoft.com/en-us/library/system.threading.synchronizationcontext.aspx

答案 1 :(得分:1)

虽然它没有同步线程,但您可以使用Control.IsDisposed检查是否可以在当前状态下调用Invoke()方法(Form继承自Control ):

if (!this.IsDisposed)
{
   // tatata...
}

为了保证它是有效的,你必须将它包装在lock(syncobject) { ... }块中以检查自定义布尔标志,同样在lock - Form_Close - {{1}}事件中更改该标志。

答案 2 :(得分:1)

我使用的是一个我称之为线程门的类,我通常会继承它,但你可以实例化它或者其他什么。这使用SynchronizationContext类。

public class ThreadGate
{
    private readonly SynchronizationContext _synchronizationContext;
    public ThreadGate()
    {
        _synchronizationContext = AsyncOperationManager.SynchronizationContext;
    }

    public void Post<T>(Action<T> raiseEventMethod, T e)
    {
        if (_synchronizationContext == null)
            ThreadPool.QueueUserWorkItem(delegate { raiseEventMethod(e); });
        else
            _synchronizationContext.Post(delegate { raiseEventMethod(e); }, null);
    }
}

我这样用:

Post(OnProgressChanged,new ProgressChangedEventArgs(1,null)));

或者如果你喜欢Action

Action<string> callback;
Post(callback,str));