【发布时间】:2013-12-18 17:16:00
【问题描述】:
在尝试使多摄像头系统与处理不同摄像头的不同线程一起工作时,我无法让信号和插槽在不同线程之间正常工作。我知道发送信号的对象和相关插槽的对象存在于不同的线程中这一事实有问题,因此我知道我可能只需要为连接找到一个合适的“连接类型”参数。最终,我发现只有使用 Qt::DirectConnection 才能让一切正常运行。
找到下面的简化代码。以下是关于一切应该如何工作的简短说明。
应用程序是应该创建所有线程并启动它们的主程序。在这个简化版本中,它只是等待工作人员通过槽“退出”完成其工作。
Worker 是执行线程任务之一的对象。在这个简化的例子中,我只是在完成计算之前等待一些时间。然后,worker 发出一个指向应用程序实例的信号,然后允许该应用程序等待所有线程并退出 QCoreApplication。
我发现如果我在第二次连接中不使用Qt::DirectConnection,那么worker的finished()信号不会触发线程的quit()槽,这意味着应用然后保持挂起等待线程。
我的问题是:为什么会这样?由于这两个对象(工作者和线程)属于不同的线程,我不应该使用 QueuedConnection 还是其他什么?我认为 DirectConnection 应该只用于属于同一线程的对象。
main.cpp:
#include <iostream>
#include <QCoreApplication>
#include "program.h"
using namespace std;
int main(int argc, char **argv) {
QCoreApplication app(argc, argv);
Program *p = new Program;
p->execute();
app.exec();
delete p;
}
程序.h
#ifndef _PROGRAM_H_
#define _PROGRAM_H_
#include <QThread>
#include <QTimer>
#include "worker.h"
class Program: public QObject {
Q_OBJECT
private:
Worker *worker;
QThread *thread;
public:
void execute();
public slots:
void quit();
};
#endif // _PROGRAM_H_
程序.cpp
#include "worker.h"
using namespace std;
void Program::execute() {
worker = new Worker();
thread = new QThread;
worker->moveToThread(thread);
cout << "Connection established: "
<< connect(thread, SIGNAL(started()), worker, SLOT(process()))
<< endl;
// slot not called if I remove the fifth parameter
// or if I put Qt::QueuedConnection
cout << "Connection established: "
<< connect(worker, SIGNAL(finished()), thread, SLOT(quit()),
Qt::DirectConnection)
<< endl;
cout << "Connection established: "
<< connect(worker, SIGNAL(finished()), this, SLOT(quit()))
<< endl;
cout << "Connection established: "
<< connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()))
<< endl;
cout << "Connection established: "
<< connect(worker, SIGNAL(finished()), thread, SLOT(deleteLater()))
<< endl;
thread->start();
}
void Program::quit() {
cout << "waiting.." << endl;
thread->wait();
cout << " .. I'm done!" << endl;
cout << "quitting from all.." << endl;
QCoreApplication::quit();
cout << " .. I'm done!" << endl;
}
#include "program_moc.cpp"
worker.h
#ifndef _WORKER_H_
#define _WORKER_H_
#include <QObject>
class Worker: public QObject {
Q_OBJECT
public slots:
void process();
signals:
void finished();
};
#endif // _WORKER_H_
worker.cpp:
#include <iostream>
#include <unistd.h>
#include "worker.h"
using namespace std;
void Worker::process() {
cout << "Worker::process() started" << endl;
usleep(1000000);
cout << "Worker::finished() being emitted" << endl;
emit finished();
cout << "Worker::process() finished" << endl;
}
#include "worker_moc.cpp"
编辑
按照@ariwez 的回答确实解决了这个特定简化示例中的问题,但它并没有解决我现在添加的稍微复杂一点的问题。
在这个例子中,
程序有自己的作业,通过使用 QTimer 定期执行。 Program还有另外一个QTimer,用来模拟用户退出程序,触发slot Program::quit()的执行。
Worker 执行自己的工作,直到他的退出标志设置为 false。这是在 Program::quit() 中完成的。
和前面的例子一样,worker 成功地完成了它的过程并发出了 finished() 信号,该信号也应该与线程的 quit() 槽连接。但是,不知何故不能执行槽,因为程序挂起等待线程。与前面的示例不同,重新定位 moveToThread 过程并不能解决问题:当且仅当我使用 Qt::DirectConnection 类型来连接 Worker::finished() 和 QThread::quit() 时,一切正常,并且我不明白为什么。
main.cpp:同上
程序.h:
#ifndef _PROGRAM_H_
#define _PROGRAM_H_
#include <QThread>
#include <QTimer>
#include "worker.h"
class Program: public QObject {
Q_OBJECT
private:
QTimer *timer, *quittingTimer;
Worker *worker;
QThread *thread;
public:
~Program();
void execute();
private slots:
void quit();
void update();
};
#endif // _PROGRAM_H_
程序.cpp:
#include <iostream>
#include <QCoreApplication>
#include "program.h"
#include "worker.h"
using namespace std;
Program::~Program() {
delete timer;
delete quittingTimer;
}
void Program::execute() {
timer = new QTimer();
timer->setInterval(500);
connect(timer, SIGNAL(timeout()), this, SLOT(update()));
worker = new Worker;
thread = new QThread;
cout << "Connection established: "
<< connect(thread, SIGNAL(started()), worker, SLOT(process()))
<< endl;
// doesn't work if I remove Qt::DirectConnection
cout << "Connection established: "
<< connect(worker, SIGNAL(finished()), thread, SLOT(quit()),
Qt::DirectConnection)
<< endl;
cout << "Connection established: "
<< connect(worker, SIGNAL(finished()), this, SLOT(quit()))
<< endl;
cout << "Connection established: "
<< connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()))
<< endl;
cout << "Connection established: "
<< connect(worker, SIGNAL(finished()), thread, SLOT(deleteLater()))
<< endl;
worker->moveToThread(thread);
timer->start();
thread->start();
// simulates user pressing key to close program
quittingTimer = new QTimer();
quittingTimer->singleShot(4000, this, SLOT(quit()));
}
void Program::quit() {
cout << "timer->stop()" << endl;
timer->stop();
cout << "worker->quit()" << endl;
worker->quit();
cout << "thread->wait()" << endl;
thread->wait();
cout << "qcore->quit()" << endl;
QCoreApplication::quit();
}
void Program::update() {
cout << "Program::update() called" << endl;
}
#include "program_moc.cpp"
worker.h:
#ifndef _WORKER_H_
#define _WORKER_H_
#include <QObject>
class Worker: public QObject {
Q_OBJECT
private:
bool quit_flag;
public:
void quit();
public slots:
void process();
signals:
void finished();
};
#endif // _WORKER_H_
worker.cpp:
#include <iostream>
#include <unistd.h>
#include <QThread>
#include "worker.h"
using namespace std;
void Worker::quit() {
quit_flag = true;
}
void Worker::process() {
quit_flag = false;
while(!quit_flag) {
cout << "Worker::process() is processing" << endl;
usleep(300000);
}
cout << "Worker::finished() is being sent" << endl;
emit finished();
cout << "Worker::finished() is sent" << endl;
}
#include "worker_moc.cpp"
编辑 2
重新阅读@ariwez 链接中的文章,我发现这是第二个示例中的问题所在。问题是主事件循环在等待线程的 QThread::finished() 信号时被中断,因此无法将 Worker::finished() 信号分派到 QThread::quit() 槽中。所以,是的,基本上我自己陷入了僵局。
【问题讨论】:
-
@ariwez:谢谢,我想我找到了问题所在。我已经读过那篇文章,但我决定再读一遍,以确保我正确理解了所有内容。我将用正确的分析更新我的问题。如果你写另一个答案(你之前的答案发生了什么?)我会选择它。
-
很高兴为您提供帮助。我不得不重新考虑我的答案,所以我把它隐藏了(希望我或其他人会给出更好的解决方案);)
-
@user2152106 我最近也有同样的问题。我在这里没有看到明确的答案。你的意思是我们不必
QThread.wait(),只需设置工人的quit_flag = true?防止死锁?
标签: c++ qt signals-slots qthread