1

我正在使用第 3 方库将数据从第 3 方输入设备传送到 Windows 窗体。我要做的是从设备收集输入数据,对其进行处理并在特定条件下向 Windows UI 线程报告正在发生的事情。我无权访问第 3 方 DLL 的源代码,但我知道主要方法在后台进程中,并且我无法将我的发现传达回我认为的主 UI 线程,因为我没有创建它?

窗体:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        // create instance of my listener
        MyListener listener = new MyListener(this);
        Controller controller = new Controller(listener);
   }
}

MyListener 类扩展了第 3 方类监听器:

public class MyListener : Listener
{
    public Form1 form;
    private Frame frame;

    // overloaded constructor
    public LeapListener(Form1 f)
    {
        form = f;
    }

    /// <summary>
    /// onFrame is the main method that runs every milisecond to gather relevant information 
    /// </summary>
    public override void onFrame(Controller controller)
    {
        // Get the most recent frame and report some basic information
        frame = controller.frame();
     }
 }

问题是我可以从 MyListener 类中的任何位置与主 UI 线程进行通信,但我无法从 onFrame 方法进行通信,因为它在后台线程上运行。有没有办法从我没有创建的后台线程中获取主线程?

我尝试了 ReportProgress,我尝试在 MyListener 上创建一个事件,并且所有尝试从 onFrame 与主 UI 线程对话都会使应用程序崩溃并给我无效的内存位置错误。

任何帮助将不胜感激。

4

1 回答 1

0

通常,尝试从处理 UI 的线程以外的线程访问 UI 对象是有问题的。这不是唯一的 Windows 问题,而是更普遍的模式。

通常的解决方案是设置某种形式的事件传播机制,其中包含更新 UI 所需的数据,并让主线程处理该任务。

我们称UI线程为UI,后台线程为BT。

你可以有一个函数从 BT 向 UI 发布事件,然后阻止 BT 直到事件被 UI 处理。这是一个使用信号量阻止 BT 直到 UI 释放它的简单系统。这种系统的优点是它很简单,您不需要一次处理来自任一线程的多个事件。不利的一面是,如果处理事件需要很长时间,则应用程序的设备采样分辨率会很差。另一种(更好的)方法是创建一个事件队列,让 BT 发布到它,然后 UI 轮询它以更新自身。它需要更多的工作,但它对 UI 长时序更具弹性。

对于第二种技术,您必须为您的事件建立一个格式,在 BT 和 UI 之间设置一个共享和互斥保护的队列,其余的应该很容易做到。

队列可能如下所示:

#include <queue>


template <typename EVENT>
class EventQueue {
   protected:
     typedef EventQueue<EVENT> self_type;
     typedef std::queue<EVENT> queue_type;
     Mutex m_;
     queue_type q_;
     void lock(){
         // lock the mutex
     }
     void unlock(){
         // unlock the mutex
     } 
   public:
     EventQueue() {
        // initialize mutex
     }
     ~EventQueue() {
        // destroy mutex
     }

     void push(EVENT &e){
         lock();
         q_.push(e);
         unlock();
     }
     EVENT &pop(){
         EVENT &e;
         lock();
         e = q_.pop();
         unlock();
         return e;
     }
     int size(){
         int i;
         lock();
         i = q_.size();
         unlock();
         return i;
     }
};

如您所见,这非常简单,您只需将上面的模板与您需要的任何 EVENT 类一起使用。

我省略了处理互斥锁的代码,这取决于您要依赖的 api。如上所述,UI 必须只轮询队列,即检查队列的大小,并在有可用事件时取出事件。在 BT 方面,您需要做的就是在您的MyListener课程中包含事件推送调用,而不是直接访问表单。

这种方法非常有效地让两个线程一起工作而不会互相踩到脚趾。

于 2012-12-14T20:38:59.667 回答