添加用于侦听网络的新线程时应用程序冻结
Application freezes when adding a new thread for listening to the network
我通过函数 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] = '[=12=]';
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。
可能是什么问题?
所提供的代码并未指明您使用哪种方法来实现线程功能。
- 我假设你的 zeroMQResponderthread 是一个 QThread 对象
- QThread 对象本身应该不 通过 moveToThread 移动到它自己。 QThread 是一个对象,应该保留在它创建 QObject 的上下文中(在本例中是您的 MainWindow)。
要解决您的问题,请创建一个新的 class,它派生自 QObject(包括声明中的 Q_OBJECT 标志),提供一个插槽来完成当前在您的 Run_zeroMQResponderThread() 并将此插槽连接到您的 QThread-Object 的 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();
将 QWidget
移动到除主线程或空线程以外的任何线程,这本来就没有用。 期间.
无论如何,您应该从 UI 中分离出代码的控制器方面。控制器将驻留在一个或多个 QObject
中。您所要做的就是将 那些 移动到一个工作线程,然后就可以了。
我通过函数 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] = '[=12=]';
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。
可能是什么问题?
所提供的代码并未指明您使用哪种方法来实现线程功能。
- 我假设你的 zeroMQResponderthread 是一个 QThread 对象
- QThread 对象本身应该不 通过 moveToThread 移动到它自己。 QThread 是一个对象,应该保留在它创建 QObject 的上下文中(在本例中是您的 MainWindow)。
要解决您的问题,请创建一个新的 class,它派生自 QObject(包括声明中的 Q_OBJECT 标志),提供一个插槽来完成当前在您的 Run_zeroMQResponderThread() 并将此插槽连接到您的 QThread-Object 的 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();
将 QWidget
移动到除主线程或空线程以外的任何线程,这本来就没有用。 期间.
无论如何,您应该从 UI 中分离出代码的控制器方面。控制器将驻留在一个或多个 QObject
中。您所要做的就是将 那些 移动到一个工作线程,然后就可以了。