【问题标题】:Cross-thread event handling on non-UI object非 UI 对象的跨线程事件处理
【发布时间】:2010-03-10 09:32:54
【问题描述】:

我在处理与引发事件的不同线程上的事件时遇到问题。但是,处理事件的对象不是 UI 对象,所以我不能使用 Invoke 来执行委托并自动切换到 UI 线程进行事件处理。

情况如下:我有一个包含多个表单的 MDI 应用程序。每个表单都有自己的控制器类,用于处理耦合表单和外部对象之间的通信。所有表单都是概览或详细表单(例如 ContactsOverview 和 ContactDetail)并共享相同的数据。

在发生错误的情况下,表单以类似向导的顺序出现,例如详细表单后跟概览表单。在以下概览表单中使用的详细表单数据发生更改,在切换到概览表单之前,这些更改需要在此处反映。一个事件从详细表单中引发,并由概览表单的控制器处理,该概览表单执行必要的 UI 元素更新。

现在,在详细信息表单中保存更改的数据可能需要一段时间,因此 UI 必须保持响应,并且应用程序的其他部分仍然可以使用。这就是开始后台工作人员处理此问题的原因。保存数据时,会在后台线程上引发事件。概览的控制器会处理这个问题,但是当 UI 需要更新时,当然会有跨线程异常。

所以我需要一种在 UI 线程上引发事件的方法,但由于处理不会发生在 UI 元素上,因此无法使用 Invoke 自动切换线程。

通过网络搜索,我找到了一种可能的解决方案,即使用生产者/消费者模式。但据我所知,这将要求每个控制器在单独的线程中侦听事件队列。由于它是一个 MDI 应用程序,理论上可以有任意数量的带有控制器的表单,我不想启动那么多线程。

欢迎提出任何建议。如果有办法避免一起使用 backgroundworker,那也是一个合适的解决方案。

感谢阅读,

凯文

【问题讨论】:

    标签: c# multithreading


    【解决方案1】:

    您可以使用SynchronizationContext,特别是SynchronizationContext.Current,将消息发布到主同步上下文(这是GUI 应用程序的主线程)。

    不幸的是,我对这个类及其用法知之甚少,无法说这是一个明确的解决方案。特别是,如果你不需要 主线程 来处理你的事件,我不知道你应该怎么做,而是一个特定的线程。

    也许WindowsFormsSynchronizationContext 类可以帮助你,它有一个公共的无参数构造函数,我认为它可能会将它与当前线程相关联,所以如果你从拥有控制器的线程构造该对象,并给出把它放到后台线程代码中,它可能会起作用。

    【讨论】:

    • 阅读这篇有用的文章 - msmvps.com/blogs/manoj/archive/2005/11/03/74120.aspx - 我很确定这种方法能够解决我的问题。据我了解,您可以使用 SynchronizationContext 来保存对调用线程的引用,并且在后台线程中的处理完成后,您可以使用 SynchronizationContext 中的引用在调用线程上启动事件。我已经在一个快速演示项目中对其进行了测试,并且可以正常工作。再次感谢!
    【解决方案2】:

    您可以在 UI 元素订阅的背景对象上设置一个事件。在事件处理程序(订阅的 - 所以它是窗口代码的一部分)中,您可以进行调用。我就是这样解决这个问题的。

    【讨论】:

    • 问题是UI元素没有订阅事件,UI元素的控制器是,它只是一个普通的c#类,没有调用方法。
    • 嗯......在这种情况下,我会说将控制器与 UI 元素完全分离是行不通的,或者 ;) 也许控制器应该得到一个指向表单的指针,所以它有一些东西可以调用...
    • 这个应用程序是在第三方框架上开发的,任何关于 UI 和控制器之间关系的更改都会导致代码更改的海啸。但如果它像你建议的那样工作,它会为我们省去很多麻烦:)
    【解决方案3】:

    你可以试试这个flag,但我认为这不是最好的主意,只是一种解决方法。

    您也可以尝试在非图形线程中实例化发布对象,这可能会解决您的问题。

    还有一件事,你不能让你的 UI 组件处理 RunWorkerCompleted(带间接)吗?

    【讨论】:

    • 你的意思是从UI元素启动后台worker,然后在RunWorkerCompleted中通过UI元素访问UI线程?
    • 从第一条评论开始,我现在明白你的意思了。问题是数据的保存会自动触发事件的引发,并且分离该部分将是相当多的工作,而不是使用我接受的 SynchronizationContext 作为答案。但这仍然是一个可行的解决方案。谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多