QThread在使用之前从未运行/完成?

时间:2011-04-23 09:30:51

标签: qt qthread

我创建了一个名为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似乎让我感到困惑。任何建议都非常受欢迎:)

3 个答案:

答案 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 LevelReusing(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次,则由于传递给该插槽的参数可能会增加内存使用率,并且某些信号可能永远不会到达该插槽