我有一个派生自QObject
,UploadWorker
的类,它已经使用推荐的在线程中运行任务的方式启动,如Qt文档中所示。
QThread* thread = new QThread();
UploadWorker* worker = new UploadWorker();
worker->moveToThread(thread);
connect(thread, SIGNAL(started()), worker, SLOT(doWork()));
现在,这完全正常。我的UploadWorker
然后尝试使用相同的技术为自己创建一个名为Uploader
的工人。
以下是标题的必要部分。
class UploadWorker : public QObject
{
Q_OBJECT
public:
// stuff
public slots:
void doWork();
signals:
void allWorkDone();
protected:
void startUploader();
};
class Uploader : public QObject
{
Q_OBJECT
public:
// stuff
public slots:
void doWork();
void finishWhenQueueIsEmpty();
};
这是UploadWorker
。
void
UploadWorker::doWork()
{
// This method is called when QThread emits started()
// Prepare the upload thread
startUploader();
// Do some important work...
// Notify the upload thread that we're done
emit allWorkDone();
}
void
UploadWorker::startUploader()
{
QThread* thread = new QThread();
Uploader* uploader = new Uploader();
uploader->moveToThread(thread);
connect(this, SIGNAL(stopped()), uploader, SLOT(stop()));
connect(uploader, SIGNAL(finished()), thread, SLOT(quit()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
connect(uploader, SIGNAL(finished()), uploader, SLOT(deleteLater()));
connect(thread, SIGNAL(started()), uploader, SLOT(doWork()));
connect(this, SIGNAL(allWorkDone()),
uploader, SLOT(finishWhenQueueIsEmpty()));
thread->start();
}
我现在的问题是,永远不会触发Uploader::finishWhenQueueIsEmpty()
广告位。除非:我删除uploader->moveToThread(thread);
。
哪里有错误?
修改的
我忘了提到线程启动时调用Uploader::doWork()
方法。它是任何其他插槽,在此示例中finishWhenQueueIsEmpty()
无法正常工作。
我修改了关于插槽名称的拼写错误。
答案 0 :(得分:7)
一个非常可能的可能性是你正在执行你的Uploader::doWork()
槽并等待调用插槽Uploader::finishWhenQueueIsEmpty()
来完成它的执行,通过一些同步机制(布尔isRunning
例如)。
您需要做的是连接您的插槽,如下所示:
connect(this, SIGNAL(allWorkDone()),
uploader, SLOT(finishWhenQueueIsEmpty()),
Qt::DirectConnection);
并在finishWhenQueueIsEmpty()
中使用互斥锁保护同步机制。
为什么?
因为在不同线程中的对象之间建立连接的默认模式是Qt::QueuedConnection
。它在这里做的是这里Uploader
类的每个槽都排队等待执行,也就是说,一旦线程返回到事件循环(即当前槽已经完成运行),队列中的下一个槽就会运行)。然后,这意味着在此模式下,调用Uploader::doWork()
然后调用Uploader::finishWhenQueueIsEmpty()
将导致程序停留在doWork()
。
使用Qt::DirectConnection
,插槽将在运行UploaderWorker::doWork()
的线程中运行,并且实际上可以在Uploader::doWork()
运行时运行。
但是你有两个线程可以访问相同的Uploader
对象,因此你需要保护每个访问/写入这个对象的部分。
答案 1 :(得分:3)
void
UploadWorker::doWork()
{
// This method is called when QThread emits started()
// Prepare the upload thread
startUploader();
// Do some important work...
// Notify the upload thread that we're done
emit allWorkDone();
}
这里的结构告诉我,在实际完成“重要工作”之前,不要让事件循环运行,这意味着不会触发任何插槽。
要么通过定期调用QApplication::processEvents();
来运行事件,要么重新设计该工作,以便通过事件循环中的一系列调用来处理它。
答案 2 :(得分:0)
在Uploader类中标记插槽:void finishedWhenQueueIsEmpty();
然后你打电话: -
connect(this, SIGNAL(allWorkDone()), uploader, SLOT(finishWhenQueueIsEmpty()));
所以一个人就完成了......'和另一个完成时......'
如果您使用此连接语法(pre Qt 5.0),那么您可以检查connect调用的返回值,或者至少在调试器中运行时查看调试输出,它会告诉您插槽是无效。
使用Qt 5连接语法,可以在编译时捕获: -
connect(this, &UploadWorker::allWorkDone() uploader, &Uploader::finishWhenQueueIsEmpty());