我已经在Qt中创建了一个Worker对象来无限期地处理视频输入,然后将其移动到QThread中以保持UI线程的运行。问题是,我对其进行了设计,以使视频捕获功能在无限循环中运行,直到被标志中断为止,并且该标志应该由Worker对象中的插槽设置,但是由于Worker对象位于无限循环内部,它永远不会处理此“退出”广告位(或者至少我认为这是正在发生的事情)。我依赖于外部库,因此将视频轮询替换为另一种方法并不是真正的选择。有人可以确认这确实是问题所在并提出解决方案吗?这是代码:
class worker : public QObject{
Q_OBJECT
public:
worker(QObject* parent = NULL);
~worker(){}
Q_SLOT void process();
Q_SLOT void stop();
private:
bool quit;
};
worker::worker(QObject *parent) : QObject(parent){
quit = false;
}
void worker::process(){
while(!quit){
//this library call puts the thread to sleep until a frame is available
WaitForVideoFrame();
}
}
void worker::stop(){
quit = true;
}
然后从UI对象中获得:
MyWorker = new worker();
QThread* thread = new QThread;
MyWorker->moveToThread(thread);
connect(thread, SIGNAL(started()), MyWorker, SLOT(process()));
QPushButton* stop_button = new QPushButton(this);
connect(stop_button, SIGNAL(clicked(bool)), MyWorker, SLOT(stop()));
thread->start();
这里的问题是,当我按下stop_button时,什么也没发生,工作程序继续运行循环。我是否可以调用一个函数来从无限循环内为事件循环产生处理时间?或对此有更好的设计/解决方案?欢迎任何建议。
答案 0 :(得分:0)
您的假设正确。每个QThread
都有自己的QEventLoop
,当您在不同线程上发出信号时,它将在目标QEventLoop
中排队等待处理。 QEventLoop
的工作人员处理完事件后,在您完成当前时段后,所有其他发出的信号将排队并进行处理。
但是,解决这个问题并不难;这需要一些注意,但是您可以安全地从QThread
派生出来,并进行长时间的轮询工作。如果您不需要采取恢复措施,QThread
甚至具有一个可以向您指示已请求中断的属性,请检查QThread::isInterruptionRequested(),例如:
class mythread : public QThread {
Q_OBJECT
public:
using QThread::QThread;
void run() override { // Main loop
while (!isInterruptionRequested()) {
//this library call puts the thread to sleep until a frame is available
WaitForVideoFrame();
}
}
}
但是要做时需要注意正确的关闭顺序(大概是在MainWindow析构函数中):
void stopThread() {
if (m_thread != nullptr) {
m_thread->requestInterruption();
m_thread->exit();
m_thread->wait();
}
}
EDIT :以上示例在假设您不需要完全正常工作的信号和插槽的情况下工作;如果您愿意,可以更新run()
方法以在等待时进行处理:
class mythread : public QThread {
Q_OBJECT
public:
using QThread::QThread;
signals:
void alive();
public slots:
void onTimeout() { qDebug() << "timeout!"; }
void run() override { // Main loop
QTimer timer;
timer.setInterval(1500);
QObject::connect(&timer, &QTimer::timeout, this, &mythread::alive);
timer.start();
while (!isInterruptionRequested()) {
//this library call puts the thread to sleep until a frame is available
WaitForVideoFrame();
// Process event queue
eventDispatcher()->processEvents(QEventLoop::AllEvents);
}
}
};
然后您可以按常规方式将其连接:
mythread t;
t.start();
QTimer timer;
timer.setInterval(1000);
QObject::connect(&t, &mythread::alive, [] {
qDebug() << "I'm alive";
});
QObject::connect(&timer, &QTimer::timeout, &t, &mythread::onTimeout);
timer.start();
答案 1 :(得分:0)
首先,这里是QAbstractEventDispatcher上的文档,该文档使排队的信号和线程间事件传播起作用。
thread()->eventDispatcher()->processEvents();
在循环中某处的上方添加一行,以便在适当的时间间隔调用该行。它先检索当前线程,然后再检索线程的事件分配器,最后强制分配器处理任何未决事件(包括传入排队的信号)。
可以使用拥有线程的requestInterruption()
函数来代替线程不安全标志。
随后,您可以呼叫QThread::isInterruptionRequested()
。如果返回true,则可以假定是时候停止进行任何工作了。
您的循环最终将看起来像这样:
void worker::process(){
while(!thread->isInterruptionRequested()){
thread()->eventDispatcher()->processEvents();
if (thread()->isInterruptionRequested())
break;
WaitForVideoFrame();
}
}
在此循环中,您可能有两个可能长时间运行的调用,即处理事件的调用和WaitForVideoFrame()
。我已经设置了循环以检查两次循环后是否都应该退出。适用标准免责声明:请在您的用例中对此性能进行测试(特别是,在循环中两次检查中断请求可能太过激进了。)