QT多线程

2019年7月8日 0 条评论 141 次阅读 0 人点赞

QT多线程

继承QThread并重写run()

全局线程

我们把和UI相同生命周期的线程叫做全局线程。

启动线程

if(m_thread->isRunning()) return;
m_thread->start();

停止线程

如果我们在线程类的run里面调用的exec()启动了事件循环,可以通过quit()通知事件循环以返回值0结束,或者通过exit(returnCode)以指定返回值结束。

如果run里面是个死循环,可以通过设置一个bool值来控制循环结束,例如:

void ThreadFromQThread::run()
{    
    {
        QMutexLocker locker(&m_lock);
        m_isCanRun = true;
    }
    while(true)
    {
        // ...
        {
            QMutexLocker locker(&m_lock);
            if(!m_isCanRun)
            {
                return ;
            }
        }
    }
}

为了防止发生冲突需要对该bool值加锁。

在外界通过修改这个bool值通知进程结束:

void ThreadFromQThread::stopImmediately()
{
    QMutexLocker locker(&m_lock);
    m_isCanRun = false;
}

删除线程对象

不要直接使用delete,可能删除时还有某个信号在事件循环中等待发送给这个对象的某个槽。可以将线程对象的父对象设置为窗体,这样Qt会自动析构删除该对象。如果想要手动删除,将线程对象的finished信号连接到其deleteLater槽,这样会在线程停止后调用deleteLater,后者会在合适的时间将对象删除。

关于deleteLater

局部线程

通过将finished信号连接至deleteLater槽,使对象内存能自动释放。

void Widget::onRunLocalBtnClicked()
{
    ThreadFromQThread *thread = new ThreadFromQThread();
    connect(thread, &ThreadFromQThread::message, this, &Widget::receiveMessage);
    connect(thread, &ThreadFromQThread::progress, this, &Widget::progress);
    connect(thread, &QThread::finished, this, &Widget::onQThreadFinished);
    connect(thread, &QThread::finished, thread, &QObject::deleteLater);
    thread->start();
}

上面的代码在多次点击按钮的时候会启动多个线程。

如果遇到点击按钮需要结束当前线程并启动一个全新线程,可以设置一个变量记录正在运行的进程。

private:
    // 记录局部线程
    ThreadFromQThread* m_currentRunLocalThread;
public slots:
    void onLocalThreadDestroy(QObject* obj);

// 开启新线程
void Widget::onRunLocalBtnClicked()
{
    if(m_currentRunLocalThread != nullptr)
    {
        m_currentRunLocalThread->stopImmediately();
    }
    ThreadFromQThread* thread = new ThreadFromQThread();
    connect(thread, &ThreadFromQThread::message, this, &Widget::receiveMessage);
    connect(thread, &ThreadFromQThread::progress, this, &Widget::progress);
    connect(thread, &QThread::finished, this, &Widget::onQThreadFinished);
    connect(thread, &QThread::finished, thread, &QObject::deleteLater);
    connect(thread, &QObject::destroyed, this, &Widget::onLocalThreadDestroy);
    m_currentRunLocalThread = thread;
    thread->start();
}

// 线程结束后将指针置空
void Widget::onLocalThreadDestroy(QObject *obj)
{
    // 之所以要进行这个判断,是因为如果是中途停止线程的话,这个函数会在onRunLocalBtnClicked()之后运行(它们在同一个线程当中)
    // m_currentRunLocalThread此时已经被赋予新的值了,不需要再置空
    if(m_currentRunLocalThread == obj)
    {
        m_currentRunLocalThread = nullptr;
    }
}

使用QObject.moveToThread

Qt推荐的方法,将要在新线程做的工作放到一个QObject子类的槽函数中,new一个子类对象(不能有父对象)和一个QThread对象,然后调用QObjectmoveToThread方法将QObject子类对象放到子线程中。

  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 &);
  };

信号与槽的连接方式

1,直接连接,当signal发射时,slot立即调用。此slot在发射signal的那个线程中被执行(不一定是接收对象生存的那个线程)

2,队列连接,当控制权回到对象属于的那个线程的事件循环时,slot被调用。此slot在接收对象生存的那个线程中被执行

3,自动连接(缺省),假如信号发射与接收者在同一个线程中,其行为如直接连接,否则,其行为如队列连接。

参考:

Qt使用多线程的一些心得

litmxs

这个人太懒什么东西都没留下

文章评论(0)