【问题标题】:QThread never runs/finishes before it can be used?QThread 在可以使用之前从不运行/完成?
【发布时间】:2011-04-23 09:30:50
【问题描述】:

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

【问题讨论】:

  • videoEncode() 信号何时发出?
  • 我最初在启动线程后立即发出了 videoEncode() 信号,但与当前问题相比,行为没有任何变化。

标签: qt qthread


【解决方案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"

【讨论】:

    【解决方案2】:

    您必须派生QThread,而不是QObject。 run() 方法是QThread 的抽象方法。

    【讨论】:

    • 是的,我知道...我正在使用自定义 QObject 的 moveToThread() 函数将实例移动到 QThread 实例。我调用函数也 run() 只是巧合。
    【解决方案3】:

    对于未来的程序员,我添加了这个答案。 Qt 中的多线程通常有 3 种方法。 Low LevelReusing(lets say Mid Level)High Level

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

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

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-16
      • 2015-05-02
      • 2020-09-28
      相关资源
      最近更新 更多