Qt多线程之moveToThread

写在前面

本节讲解以Qt信号和槽机制:connect函数中的第五个参数QtConnectionType https://blog.csdn.net/QQ1402369668/article/details/87805654 为基础。

QThread描述

Detailed Description:

The QThread class provides a platform-independent way to manage threads.
A QThread object manages one thread of control within the program. QThreads begin executing in run(). By default, run() starts the event loop by calling exec() and runs a Qt event loop inside the thread.
You can use worker objects by moving them to the thread using QObject::moveToThread().
翻译:QThread类提供了一种与平台独立的方式去管理线程。
一个QThread对象管理一个线程的控制在程序中。
QThread在run()函数中开始执行。
默认的,run()函数开始事件循环通过调用exec()函数,并且在线程中运行了一个Qt的事件循环。
class Worker : public QObject
  {
      Q_OBJECT

  public slots:
      void doWork(const QString &parameter) {
          QString result;
          /* ... here is the expensive or blocking operation ... */
          emit resultReady(result);
      }

  signals:
      void resultReady(const QString &result);
  };  
class Controller : public QObject
  {
      Q_OBJECT
      QThread workerThread;
  public:
      Controller() {
          Worker *worker = new Worker;
          worker->moveToThread(&workerThread);
          connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
          connect(this, &Controller::operate, worker, &Worker::doWork);
          connect(worker, &Worker::resultReady, this, &Controller::handleResults);
          workerThread.start();
      }
      ~Controller() {
          workerThread.quit();
          workerThread.wait();
      }
  public slots:
      void handleResults(const QString &);
  signals:
      void operate(const QString &);
  };
The code inside the Worker's slot would then execute in a separate thread. However, you are free to connect the Worker's slots to any signal, from any object, in any thread. It is safe to connect signals and slots across different threads, thanks to a mechanism called queued connections.
翻译:Worker槽函数的代码将在一个不同的线程中执行。
无论如何,你是自由的去连接Worker的槽函数到任何的信号,从任何对象,在任何线程中。
在不同的线程中连接信号和槽是安全的,,这要得益于一个叫队列连接(注意:此处即是上节我们讲的Qt::QueuedConnection!!!)的机制。
Another way to make code run in a separate thread, is to subclass QThread and reimplement run(). For example:
翻译:另一种让代码在一个不同的线程中运行的方法,是写一个QThread的子类并且重写run()函数。
  class WorkerThread : public QThread
  {
      Q_OBJECT
      void run() Q_DECL_OVERRIDE {
          QString result;
          /* ... here is the expensive or blocking operation ... */
          emit resultReady(result);
      }
  signals:
      void resultReady(const QString &s);
  };

  void MyObject::startWorkInAThread()
  {
      WorkerThread *workerThread = new WorkerThread(this);
      connect(workerThread, &WorkerThread::resultReady, this, &MyObject::handleResults);
      connect(workerThread, &WorkerThread::finished, workerThread, &QObject::deleteLater);
      workerThread->start();
  }
In that example, the thread will exit after the run function has returned. There will not be any event loop running in the thread unless you call exec().
翻译:在这个例子中,run()函数返回时这个线程将退出。
线程中将不会有任何事件循环除非你调用exec()函数。
It is important to remember that a QThread instance lives in the old thread that instantiated it, not in the new thread that calls run(). This means that all of QThread's queued slots will execute in the old thread. Thus, a developer who wishes to invoke slots in the new thread must use the worker-object approach; new slots should not be implemented directly into a subclassed QThread.
翻译:一定要记得QThread对象生存在实例化它的老线程中,不在调用run()函数的新线程中。
这意味着所有QThread的队列槽函数将在老线程中执行。
这样,开发者希望在新线程中执行槽函数必须用worker-object的方法。
新槽函数不能直接在子类化的QThread中执行 。
When subclassing QThread, keep in mind that the constructor executes in the old thread while run() executes in the new thread. If a member variable is accessed from both functions, then the variable is accessed from two different threads. Check that it is safe to do so.
翻译:当子类化QThread时,注意构造者在老线程中执行,而run()函数在新线程中执行。
一个成员变量双方函数都是可以访问的,两个不同的线程都可以访问这个变量;
这样做的时候检查它是安全的。

void QObject::moveToThread(QThread *targetThread) 函数:

Changes the thread affinity for this object and its children. The object cannot be moved if it has a parent. Event processing will continue in the targetThread.
翻译:改变线程亲和力对于这个对象和它的子类。
这个对象不能被移动如果他有父对象。
事件处理将继续在targetThread线程中执行。


To move an object to the main thread, use QApplication::instance() to retrieve a pointer to the current application, and then use QApplication::thread() to retrieve the thread in which the application lives. For example:	
	myObject->moveToThread(QApplication::instance()->thread());
翻译:把一个对象移动到主线程,用QApplication::instance()函数取出指向当前应用程序的指针,并且调用QApplication::thread()函数取出这个应用程序所生存的线程。
  

If targetThread is zero, all event processing for this object and its children stops.
翻译:如果targetThread是0(NULL),这个对象和它的子对象的所有事件处理都会停止。


Note that all active timers for the object will be reset. The timers are first stopped in the current thread and restarted (with the same interval) in the targetThread. As a result, constantly moving an object between threads can postpone timer events indefinitely.
翻译:注意这个对象中所有活着的定时器都将被重置。
定时器首先会在当前线程中停止并且在 targetThread线程中重启(和原来时间间隔相同)。


A QEvent::ThreadChange event is sent to this object just before the thread affinity is changed. You can handle this event to perform any special processing. Note that any new events that are posted to this object will be handled in the targetThread.
翻译:线程的亲和力刚刚改变前,一个QEvent::ThreadChange的事件被发送到这个线程。
你可以操作这个事件去处理任何特殊的处理。
注意任何被发送到这个对象的新事件将在targetThread中被操作。


Warning: This function is not thread-safe; the current thread must be same as the current thread affinity. In other words, this function can only "push" an object from the current thread to another thread, it cannot "pull" an object from any arbitrary thread to the current thread.
翻译:注意:这个函数不是线程安全的;
当前的线程必须和当前线程的亲和力相同。
也就是说,这个函数只能把一个对象从当前线程推进另外一个线程,它不能把一个对象从任何随意的线程推进到当前线程。

实例用法:

现在有一个耗时操作,为了防止在做此耗时操作时,避免主界面被用户点击导致程序卡死。所以需要把耗时操作放在子线程中。

mainwindow.cpp:

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    qDebug()<<u8"主线程ID"<<QThread::currentThreadId();

    m_work.moveToThread(&m_workThread);

    connect(this,SIGNAL(operate(int)),&m_work,SLOT(doWork(int)));
    connect(&m_work,SIGNAL(resultReady(QString)),this,SLOT(handleResults(QString)));

    m_workThread.start();
}

MainWindow::~MainWindow()
{
    delete ui;

    m_workThread.quit();
    m_workThread.wait();
}

void MainWindow::handleResults(QString result)
{
    qDebug()<<u8"主线程收到子线程发送的信号"<<u8" 结束";
}

void MainWindow::on_pushButton_clicked()
{
    qDebug()<<u8"主线程发信号让子线程做事";
    emit operate(6);
}

mainwindow.h:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "worker.h"
#include <QThread>
#include <QDebug>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private:
    Ui::MainWindow *ui;

    QThread m_workThread;
    Work m_work;

private slots:
    void handleResults(QString );

    void on_pushButton_clicked();

signals:
    void operate(int );
};

#endif // MAINWINDOW_H

work.h

#ifndef WORKER_H
#define WORKER_H

#include <QObject>
#include "Windows.h"
#include <QThread>
#include <QDebug>

class Work : public QObject
{
    Q_OBJECT
public:
    Work()
    {

    }

private slots:
    void doWork(int parameter)
    {
        QString result;

        qDebug()<<u8"子线程做事"<<u8" 线程ID"<<QThread::currentThreadId();

        //耗时操作
        Sleep(1000*parameter);

        qDebug()<<u8"子线程做完了事,发送信号通知主线程";
        emit resultReady(result);
    }

signals:
    void resultReady(QString result);
};

#endif // WORKER_H

运行结果:

在这里插入图片描述

结束

猜你喜欢

转载自blog.csdn.net/QQ1402369668/article/details/87903305