添加新线程以收听网络时应用程序冻结

时间:2016-08-16 15:23:49

标签: c++ c multithreading qt zeromq

我在我的应用程序中添加了一个新线程,使用Qt通过函数Start_zeroMQResponderThread

开发
// this function adds the new thread
void MainWindow::Start_zeroMQResponderThread()
{

    moveToThread(&zeroMQResponderthread);
    QObject::connect(&zeroMQResponderthread, SIGNAL(started()), this, SLOT(Run_zeroMQResponderThread())); //cant have parameter sorry, when using connect
    zeroMQResponderthread.start();
}

我在MainWindow的构造函数中调用了这个函数,以确保在应用程序的开头创建了该线程:

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
....

    // This is the sender which do not need to be run in a separate thread
    context = zmq_ctx_new();
    requester = zmq_socket(context, ZMQ_PAIR);
    rc = zmq_connect(requester, "tcp://10.131.7.97:5555"); 
...
    //  This is the starting of the thread that will listens to the network to 
    //  capture received messages using ZeroMQ
    Start_zeroMQResponderThread();
}

最后,这是函数Run_zeroMQResponderThread():它在单独的线程中运行。它使用ZeroMQ启动无限循环来检测已发送的消息,并使用Windows Text To Speech API(SAPI)将它们转换为语音消息:

void MainWindow::Run_zeroMQResponderThread() {

    ISpVoice * pVoice = NULL;
    if (!FAILED(::CoInitialize(NULL)))
    {
        HRESULT hr = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void **)&pVoice);
    }


    void *context = zmq_ctx_new();
    void *responder = zmq_socket(context, ZMQ_PAIR);
    int rc = zmq_bind(responder, "tcp://*:5555");


    printf("Receiver: Started\n");

    char buffer[128];
    wchar_t wtext[128];
    while (true)
    {
        int num = zmq_recv(responder, buffer, 128, 0);

        if (num > 0)
        {
            buffer[num] = '\0';
            printf("Receiver: Received (%s)\n", buffer);



            mbstowcs(wtext, buffer, strlen(buffer) + 1);//Plus null
            LPWSTR ptr = wtext;
            HRESULT hr;
            if (pVoice)
                hr = pVoice->Speak(ptr, SPF_DEFAULT, NULL);

            if (!SUCCEEDED(hr))
                std::cout << "speak error" << hr << std::endl;
        }
    }

    pVoice->Release();
    pVoice = NULL;
    ::CoUninitialize();

    zmq_close(responder);
    zmq_ctx_destroy(context);


}

在添加此功能之前,应用程序运行良好。但是在应用程序开始时添加它冻结后甚至没有显示应用程序的主UI。

可能是什么问题?

2 个答案:

答案 0 :(得分:3)

QWidget移动到除主线程或空线程之外的任何线程都无法工作。的期间

无论如何,您应该从UI中分析出代码的控制器方面。控制器将驻留在一个或多个QObject中。您所要做的就是将那些移动到工作线程,然后重新设置。

答案 1 :(得分:2)

提供的代码并未指出您使用哪种方法来实现线程功能。

  1. 我假设你的zeroMQResponderthread是一个QThread对象
  2. QThread-Object本身应 NOT 通过moveToThread移动到自身。 QThread是一个对象,它应该保留在创建QObject的上下文中(在这种情况下是你的MainWindow)。
  3. 要解决您的问题,请创建一个派生自QObject的新类(包括声明中的Q_OBJECT标志),给出一个插槽,该插槽执行当前在Run_zeroMQResponderThread()中完成的工作,并将此插槽连接到您的QThread-对象的开始()信号。

    它应该是这样的:

    // parent of the QObject superclass should be 0 upon creation
    WorkObject *wobject = new WorkObject(); 
    // using 'this' so the workerthread gets deleted when the mainwindow is deleted
    QThread *worker = new QThread(this); 
    // move workobject to worker
    wobject->moveToThread(worker);
    // do work as soon as worker starts
    QObject::connect(worker, SIGNAL(started()), wobject, SLOT(doWork()));
    // delete workobject when worker finishes
    QObject::connect(worker, SIGNAL(finished()), wobject, SLOT(deleteLater()));
    // start worker
    worker->start();