2

我创建了一个名为 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 调用 encode() 的 set() 函数。

我对线程并不陌生,我使用过本机 Windows 和 Linux 线程,以及各种跨平台实现的线程,但 QThreads 似乎真的让我感到困惑。任何建议都非常受欢迎:)

4

3 回答 3

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"
于 2011-05-06T07:17:49.737 回答
0

你必须导出QThread,不是QObject。run() 方法是QThread.

于 2011-04-24T02:58:44.730 回答
0

对于未来的程序员,我正在添加这个答案。Qt 中的多线程通常有 3 种方法。低级重用(比方说中级)高级

低级还包括两种不同的方法。在 Low level 你可以继承 QThread 类并提供你想要在里面并行运行的代码void QThread::run() override;。调用QThread::start()您的驱动类后,run将执行代码。

Qt 中低级多线程的另一种方法是在 Qt 的事件循环上进行中继。通过这种方式,您可以创建一个由 驱动的类,QObject不一定是 fromQThread然后QThread使用this将该类移动到新的类。之后,您将调用start()QThread对象(此 QThread 对象是类型的对象QThread。您不必QThread在这里进行子类化。只需实例化一个对象。我相信这是您在代码中误解的地方)。

调用start()您的对象后,它的事件循环在另一个线程中开始,并且只要您不将其用作 this 的最后一个参数,来自您代码中连接到驱动类插槽的任何位置的信号都将在该事件循环并行QThread运行。QObjectQt::DirectConnection

这两种方法各有优缺点。子类化QThread和使用Qthread::run()时,如果您的代码在循环内run运行while(true),则处理器的一个线程将始终被Qthread::run()代码占用,而不是在程序中其他线程的线程池中。

QObject::moveToThread()方法对大多数情况很有用。但是,如果QObject驱动类的插槽将被非常频繁地调用,例如每秒 100 或 1000 次,则内存使用量会增加,因为可能会传递给插槽的参数,并且其中一些信号可能永远不会到达插槽。

于 2020-07-05T08:32:30.657 回答