我创建了一个名为EncodeThread的自定义QObject类,其外观如下:
class EncodeThread : public QObject {
Q_OBJECT
public:
void set(SWSL::Video* v, QStringList f, QDir vDir);
void run();
public slots:
void encode();
signals:
void encodeProgress(int i);
private:
SWSL::Video* video;
QStringList files;
QDir videoDir;
};
很明显,此类用于使用外部库对视频进行编码。 Encode()包含实际的编码例程,run()是我在故障排除时添加的一个函数,虽然它显然不起作用:
void EncodeThread::run() {
if (currentThread() != this) {
// caller is in different thread.
QMetaObject::invokeMethod(this, "encode", Qt::QueuedConnection);
}
else {
encode();
}
}
问题是当我在EncodeThread实例上使用QThread和moveToThread()函数时,似乎没有任何事情发生。没有写入数据,实例永远不会发出应该将编码文件保存到磁盘的信号。
encThread.set(video, files, videoDir);
connect(&encThread, SIGNAL(encodeProgress(int)), cookVideoProgress, SLOT(setValue(int)));
connect(&encThread, SIGNAL(finished()), this, SLOT(videoCookEnd()));
connect(this, SIGNAL(videoEncode()), &encThread, SLOT(encode()));
encThread.moveToThread(&thread);
thread.start();
以上是整个设置的启动方式。 EncThread和线程变量在MainWindow类中声明。在尝试使用信号从主线程调用encode()并且QMetaObject失败后,我已经使EncodeThread的set()函数调用了encode()。
我不熟悉线程,使用本机Windows和Linux线程,以及各种跨平台实现,但QThreads似乎让我感到困惑。任何建议都非常受欢迎:)
答案 0 :(得分:1)
可能会迟到对你有任何帮助,但这里有一个小型的演示程序,可以使EncoderThread
课程起作用。它可能与您的设计(您的问题只有片段)完全不相符,但它演示了在自己的线程上运行对象实例,并通过信号/插槽连接不同线程上的2个对象,让它们进行通信:
#include <stdio.h>
#include <QObject>
#include <QThread>
#include <QtCore/QCoreApplication>
// QSleeper is just a toy utility class that makes the
// protected QThread::sleep() family of functions
// publicly accessible. It's only use is for demo
// programs like this
class Sleeper : QThread
{
public:
static void sleep(unsigned long secs) { QThread::sleep(secs); }
static void msleep(unsigned long msecs) { QThread::msleep(msecs); }
static void usleep(unsigned long usecs) { QThread::usleep(usecs); }
};
// an Encoder class that maintains itself on is own thread
class EncodeThread : public QObject {
Q_OBJECT
public:
EncodeThread();
public slots:
void encode();
signals:
void encodeProgress(int i);
void finished();
private:
QThread myThread;
};
EncodeThread::EncodeThread() {
moveToThread(&myThread);
myThread.start();
}
void EncodeThread::encode()
{
printf("EncodeThread::encode() on thread %u\n", (unsigned int) QThread::currentThreadId());
for (int i = 0; i < 6; ++i) {
// encode for 1 second or so
printf("EncodeThread::encode() working on thread %u\n", (unsigned int) QThread::currentThreadId());
Sleeper::sleep(1);
emit encodeProgress(i);
}
emit finished();
printf("EncodeThread::encode() - done\n");
}
// a controller to manage and monitor an EncoderThread instance
class VideoEncoderController : public QObject
{
Q_OBJECT
public:
void start();
public slots:
void setValue(int);
void encodingDone();
signals:
void encodingBegin();
};
void VideoEncoderController::start()
{
printf("VideoEncoderController::start() on thread %u\n", (unsigned int) QThread::currentThreadId());
emit encodingBegin();
}
void VideoEncoderController::setValue(int x)
{
printf("VideoEncoderController::setValue(int %d) on thread %u\n", x, (unsigned int) QThread::currentThreadId());
}
void VideoEncoderController::encodingDone()
{
printf("VideoEncoderController::encodingDone() on thread %u\n", (unsigned int) QThread::currentThreadId());
}
// a demo program that wires up a VideoEncoderController object to
// an EncoderThread
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
EncodeThread encThread;
VideoEncoderController controller;
QObject::connect(&encThread, SIGNAL(encodeProgress(int)), &controller, SLOT(setValue(int)));
QObject::connect(&encThread, SIGNAL(finished()), &controller, SLOT(encodingDone()));
QObject::connect(&controller, SIGNAL(encodingBegin()), &encThread, SLOT(encode()));
printf("hello world on thread %u\n", (unsigned int) QThread::currentThreadId ());
controller.start();
return a.exec();
}
#include "main.moc"
答案 1 :(得分:0)
您必须导出QThread
,而不是QObject
。 run()方法是QThread
的抽象方法。
答案 2 :(得分:0)
对于将来的程序员,我要添加此答案。在Qt中,通常有3种方法来实现多线程。 Low Level,Reusing(lets say Mid Level)和High Level。
低级别还包括两种不同的方法。在低级中,您可以继承QThread类,并提供要在void QThread::run() override;
中并行运行的代码。在驱动类上调用QThread::start()
后,将执行run
中的代码。
Qt中低级多线程的另一种方法是中继Qt的事件循环。这样,您可以创建从QObject
驱动的类,而不必从QThread
驱动,然后使用this将该类移动到新的QThread
。然后,您将在该start()
对象上调用QThread
(此QThread对象是类型为QThread
的对象。您不必在这里子类QThread
。只需实例化即可。一个对象。我认为这是您在代码中误解的地方。
在start()
对象上调用QThread
之后,其事件循环在另一个线程中开始,并且从代码中任何与QObject
驱动类插槽连接的信号都将在该线程中运行只要您不使用Qt::DirectConnection
作为this的最后一个参数,事件循环就会并行进行。
这两种方法各有优缺点。子类化QThread
并使用Qthread::run()
时,如果run
中的代码在while(true)
循环内运行,则处理器的线程之一将始终被{{1 }}代码,而不是程序中其他线程的线程池中的代码。
在大多数情况下,Qthread::run()
方法很有用。但是,如果QObject::moveToThread()
驱动的类的插槽将被非常频繁地调用,例如每秒100或1000次,则由于传递给该插槽的参数可能会增加内存使用率,并且某些信号可能永远不会到达该插槽