使用工作线程的多线程库无法与UI线程通信

时间:2012-12-14 18:44:30

标签: multithreading communication windowsformsintegration

我正在使用第三方库将数据从第三方输入设备传送到Windows窗体。我要做的是从设备收集输入数据,处理它并给定某些条件报告回Windows UI线程发生了什么。我无法访问第三方DLL的源代码,但我知道主要方法是在后台进程上,我无法将我的发现传回主UI线程我认为因为我没有创建它? / p>

Windows窗体:

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

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

扩展第三方类Listener的MyListener类:

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线程进行通信都会使应用程序崩溃并给我带来无效的内存位置错误。

非常感谢任何帮助。

1 个答案:

答案 0 :(得分: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课程中包含事件推送调用,而不是直接访问表单。

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