【问题标题】:C#: How to prevent Invoke from failingC#:如何防止调用失败
【发布时间】:2009-09-07 18:49:21
【问题描述】:

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

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 属性或我可以在调用之前检查的内容?

【问题讨论】:

    标签: c# winforms multithreading event-handling


    【解决方案1】:

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

    • 创建一个全局 SynchronizationContext 变量(比如说 syncContext)
    • 将其初始化为表单的线程同步'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 ;)
    • 你会在表单中还是在后台工作的类中这样做?
    • this.syncContext.Post 替换 InvokeRequired..Invoke 模式。您只需保存对主线程的 SynchronizationContext 的引用(例如在表单构造函数中),并将您希望在主线程中执行的任何代码提供给该 SynchronizationContext 的 Post 函数。
    【解决方案2】:

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

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

    为确保其有效,您必须将其包装在 lock(syncobject) { ... } 块中以检查自定义布尔标志,并同样在 Form_Close 事件内的 lock-statement 中更改该标志。

    【讨论】:

    • IsDisposed失败时有没有检查调试器中的状态?
    【解决方案3】:

    我使用了一个我称之为线程门的类,我通常从它继承,但你可以实例化它或其他任何东西。这使用了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<string> callback;
    Post(callback,str));
    

    【讨论】:

    • 有趣的解决方案。这个 ThreadGate 是在 gui 中使用,还是由后台工作的类使用?
    • 两者,从技术上讲,但我在做后台工作的类中使用它,然后我不必担心调用所需的模式。我发布的答案只有在有很多事件时才真正变得必要。但它显然可以用于单个事件。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-10-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-09
    • 1970-01-01
    • 2011-02-09
    相关资源
    最近更新 更多