在另一个 QThread 上运行成员方法时,无法将事件发送到不同线程拥有的对象

Cannot send events to objects owned by a different thread while runing a member method on another QThread

我需要 运行 我的 MainWindow class 中的一个方法,在不同的线程中,因为它是一个耗时长的过程。

这是我试过的:

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    initGui(ui);

    // Create background worker thread
    backgroundWorker = QThread::create([this] {
        backgroundMethod();
    });

    // Define when finished with work
    connect(backgroundWorker, &QThread::finished, [this] () {
        qDebug() << "Background method has finished";

        // Stop movie
        ui->lblLoading->movie()->stop();

        // go to next screen
        ui->tabbarMainWidget->tabBar()->setCurrentIndex(1);

        //show backup icon files
        if(filesToBackup.size() > 0) {
            qDebug() << "There are files to backup!";
            ui->lblInfoImage->show();
        }
    });

    // Start worker thread
    backgroundWorker->start();
}

backgroundMethod

void MainWindow::backgroundMethod() {
    for (int i = 0; i < 10; i++) {
        qDebug() << "Hello World";
    }
}

我省略了很多没有必要的代码。基本逻辑如下:

  1. 使用QThread::create()

  2. 开始新线程
  3. 运行 backgroundMethod() 直到完成,同时有 UI 空闲时间用于其他工作。

  4. backgroundMethod() 完成后,QThread 应该发出 finished() 信号。

  5. 我在 backgroundWorker 线程的 finished() 和 lambda 之间设置了一个连接到 运行 更多代码。

问题:

Background method has finished

QObject::killTimer: Timers cannot be stopped from another thread

ASSERT failure in QCoreApplication::sendEvent: "Cannot send events to objects owned by a different thread. Current thread 0x0x2801d950. Receiver 'lblInfoImage' (of type 'QLabel') was created in thread 0x0x2688c4b0", file kernel\qcoreapplication.cpp, line 578 04:11:28: The program has unexpectedly finished.

简而言之,我正在 backgroundWorker 线程上访问 lblInfoImage。我知道使用 signal/slot mechanism 应该可以解决这个问题,我的使用是正确的。

我不确定为什么会这样,我需要一些帮助来理解我做了什么导致了这个问题以及我该如何解决它

问题很简单:您在非 UI 线程上执行 UI 代码,这在 Qt 中是严格禁止的(以及许多其他跨不同语言的 UI 框架)。发生这种情况是因为您连接错误:

connect(backgroundWorker, &QThread::finished, [this] () {
    ...
});

那个连接意味着:每当QThread发出一个finished信号运行这个函数。问题是,它将 运行 发出信号上下文中的函数是另一个线程,而不是 backgroundWorker 所在的线程。所以你必须提供 UI 线程上下文来接收这个信号:

connect(backgroundWorker, &QThread::finished, this, [this] () {
        ...
    });

现在提供的函数将在 UI 线程 (this) 的上下文中执行。