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

Posted

技术标签:

【中文标题】添加新线程以侦听网络时应用程序冻结【英文标题】:Application freezes when adding a new thread for listening to the network 【发布时间】:2016-08-16 15:23:49 【问题描述】:

我通过函数Start_zeroMQResponderThread向我使用Qt开发的应用程序添加了一个新线程:

// 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。

可能是什么问题?

【问题讨论】:

你为什么在你的MainWindow 上打电话给moveToThread?这将尝试将QWidget 派生对象(MainWindow)移动到应用程序执行主线程以外的线程上。 Qt 通常不喜欢这样。我并不是说这绝对是问题所在,但它看起来确实很奇怪。 @G.M.并不是 Qt“一般”“不喜欢那样”。它不起作用,而且从来没有打算起作用。就是这样。 【参考方案1】:

QWidget 移动到除主线程或空线程之外的任何线程从来都不会起作用。 期间

无论如何,您都应该从 UI 中剔除代码的控制器方面。控制器将驻留在一个或多个QObjects 中。您所要做的就是将 那些 移到工作线程中,然后就可以设置了。

【讨论】:

这个。不多也不少。 谢谢,+1。非常有用。【参考方案2】:

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

    我假设您的 zeroMQResponderthread 是一个 QThread 对象 QThread-Object 本身应该通过 moveToThread 移动到它自己。 QThread 是一个对象,应该保留在它创建 QObject 的上下文中(在这种情况下是您的 MainWindow)。

要解决您的问题,请创建一个派生自 QObject 的新类(包括声明中的 Q_OBJECT 标志),提供一个插槽来执行当前在 Run_zeroMQResponderThread() 中完成的工作,并将此插槽连接到您的 QThread-对象的 started() 信号。

应该是这样的:

// 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();

【讨论】:

谢谢,+1。非常有用。

以上是关于添加新线程以侦听网络时应用程序冻结的主要内容,如果未能解决你的问题,请参考以下文章

如何将代码发送到 3ds Max 脚本侦听器

TestFlight 应用程序在启动时冻结 [关闭]

QT QThread::isrunning 冻结程序在 Pi

COM 自动化 Excel 2010 冻结

c#多线程应用程序中的界面冻结

侦听 COM 端口时跨线程操作无效[重复]