关于 qt线程

2 篇文章 0 订阅
  • 现在的 qt 是事件驱动的,所以建议使用 自定义Worker(继承QObject) + QThread 类完成线程操作 (可在 worker 类加入定时器 QTimer)
  • 所有线程操作都放在 Worker 类中(这样可以区分线程操作 和 主线程控制)
  • 需要通信使用 信号和槽 解决
创建线程
创建worker
worker完成
退出线程
是否超时
删除worker
删除线程

Worker :

class Worker : public QObject
{
    Q_OBJECT
public:
    explicit Worker() : QObject(nullptr)
    {
        FUNC_TRACE;
        m_timer = new QTimer(this);
        connect(m_timer,&QTimer::timeout,this,&Worker::doWork);
        m_timer->setInterval(500);
    }
    ~Worker(){
        FUNC_TRACE;
    }

public Q_SLOTS:
    void startWork(){
        FUNC_TRACE;
        m_timer->start();
    }
    void stopWork(){
        FUNC_TRACE;
        m_stopRequested = true;
    }
    void terminateWork(){
        FUNC_TRACE;
        m_stopRequested = true;
        m_timer->stop();

        //最后尝试干净的退出
        m_msgQueue.clear();//(最后的挣扎)
        qDebug()<<"terminated";
        emit terminated();
    }

    void enqueueMessage(const QString& msg){
        FUNC_TRACE;
        m_msgQueue.enqueue(msg);//(为什么不加锁,因为走事件队列将数据给传送过来,事件队列是线程安全的)
    }

Q_SIGNALS:
    void dataReady(const QString& ddd);//数据加工完成
    void done();//工作结束
    void terminated();//已被终结

private Q_SLOTS:
    void doWork(){
        FUNC_TRACE;
        if(m_msgQueue.isEmpty()){
            //空闲
            if(m_stopRequested){
                m_timer->stop();//首先停止timer,防止done连接的处理时间过长有一次进入doWork
                qDebug()<<"done";
                emit done();//空闲后停工
            }
            return;
        }
        //Q_ASSERT(! m_msgQueue.isEmpty());
        QString sss = m_msgQueue.dequeue();
        qDebug()<<"doMessageWork:"<<sss;
        //拼接线程,时间信息
        QString ttt = QString("thead[%1]-[%2]:%3")
                          .arg(reinterpret_cast<quintptr>(QThread::currentThreadId()))
                          .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss zzz"))
                          .arg(sss);
        emit dataReady(ttt);
    }
private:
    QTimer *m_timer;

    bool m_stopRequested = false;
    QQueue<QString> m_msgQueue;
};

Controller :

class Controller : public QObject
{
    Q_OBJECT
public:
    explicit Controller(QObject *parent = nullptr) : QObject(parent)
    {
        FUNC_TRACE;
        m_thread  = new QThread(this);

        m_worker = new Worker();
        m_worker->moveToThread(m_thread);
        //connect(m_worker,&Worker::terminated, m_thread,&QThread::quit);
        //connect(m_worker,&Worker::done,       m_thread,&QThread::quit);
        //connect(m_thread,&QThread::finished, m_worker,&Worker::deleteLater);
        connect(m_worker,&Worker::done,       this,&Controller::terminateThread);
        connect(m_worker,&Worker::terminated, this,&Controller::terminateThread);
        connect(m_worker,&Worker::dataReady,  this,&Controller::onDataReady);//数据往来
        connect(this,&Controller::showMessage,     m_worker,&Worker::enqueueMessage);//数据往来
        connect(this,&Controller::startWorker,     m_worker,&Worker::startWork);
        connect(this,&Controller::stopWorker,      m_worker,&Worker::stopWork);
        connect(this,&Controller::terminateWorker, m_worker,&Worker::terminateWork);
    }
    ~Controller(){
        FUNC_TRACE;
    }

    void startWorkerAsync(){
        FUNC_TRACE;
        m_thread->start();
        emit startWorker(QPrivateSignal());
    }
    void stopWorkerAsync(){
        FUNC_TRACE;
        emit stopWorker(QPrivateSignal());
    }
    void terminateWorkerAsync(){
        FUNC_TRACE;
        emit terminateWorker(QPrivateSignal());
    }
    void terminateThread(){
        FUNC_TRACE;
        if(m_worker){
            m_worker->deleteLater();
        }

        m_thread->quit();
        if(! m_thread->wait(100)){
            m_thread->terminate();
            m_thread->wait(100);
        }
    }

    bool waitForWorkerDone(int timeout){
        FUNC_TRACE;
        if(! m_worker){
            return true;
        }
        bool rrr = false;

        QEventLoop loop;
        connect(m_worker,&Worker::done, &loop,[&rrr,&loop](){
            rrr = true;
            loop.quit();
        });
        QTimer::singleShot(timeout, &loop,&QEventLoop::quit);
        loop.exec();

        return rrr;
    }
    bool waitForWorkerTerminated(int timeout){
        FUNC_TRACE;
        if(! m_worker){
            return true;
        }
        bool rrr = false;

        QEventLoop loop;
        connect(m_worker,&Worker::terminated, &loop,[&rrr,&loop](){
            rrr = true;
            loop.quit();
        });
        QTimer::singleShot(timeout, &loop,&QEventLoop::quit);
        loop.exec();

        return rrr;
    }

Q_SIGNALS:
    void startWorker(QPrivateSignal);//(不允许类外部调用)
    void stopWorker(QPrivateSignal); //(不允许类外部调用)
    void terminateWorker(QPrivateSignal);//(不允许类外部调用)

    void showMessage(const QString& msg);

private Q_SLOTS:
    void onDataReady(const QString& ddd){
        FUNC_TRACE;
        qDebug()<<QString("data-ready:%1").arg(ddd);
    }
private:
    QThread *m_thread;
    Worker *m_worker;
};

附:

#define FUNC_TRACE \
qDebug()<<QString("[%1][%2]%3").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss zzz")).arg((quintptr)(QThread::currentThreadId())).arg(__FUNCTION__)

总结:

  • woker->moveToThread() 后,所有调用需要支持多线程,不可直接调用,可使用信号槽(建议)或 加锁操作(特麻烦,特容易出现死锁)
  • 利用connect时对象在不同线程时,自动使用Qt::QueuedConnection机制(参数复制一份在消息队列中) + 消息队列支持多线程,将进出参数放在信号槽中

worker

起始就几句话(其他全是处于某种目的写的)

m_timer = new QTimer(this);
connect(m_timer,&QTimer::timeout,this,&Worker::doWork);
m_timer->setInterval(500);
m_timer->start();
m_timer->stop();

controller 关键函数

控制 worker 开始,停止,也是就几句话(其余都是辅助作用)
可以将一些业务逻辑和controller合并(即controller可以是业务对象)

m_thread  = new QThread(this);

m_worker = new Worker();
m_worker->moveToThread(m_thread);
connect(m_worker,&Worker::terminated, m_thread,&QThread::quit);//自动停止thread(疑问两次连接deleteLater会不会出问题)
connect(m_worker,&Worker::done,       m_thread,&QThread::quit);//自动停止thread
connect(m_thread,&QThread::finished, m_worker,&Worker::deleteLater);//析构worker
connect(this,&Controller::startWorker,     m_worker,&Worker::startWork);//启动
connect(this,&Controller::stopWorker,      m_worker,&Worker::stopWork);//停止
connect(this,&Controller::terminateWorker, m_worker,&Worker::terminateWork);//强制终止
connect(this,&Controller::showMessage,     m_worker,&Worker::enqueueMessage);//传入数据
m_thread->start();//启动线程事件循环 (先启动循环,大不了空跑几圈)
emit startWorker(QPrivateSignal());//启动worker(其实是启动timer向thread消息队列中丢timeout消息)
emit stopWorker(QPrivateSignal());
emit terminateWorker(QPrivateSignal());
if(m_worker){
   m_worker->deleteLater();//(有疑问,不太清楚可不可行,我觉得行)
}

m_thread->quit();//退出事件循环
if(! m_thread->wait(100)){
   m_thread->terminate();//退出失败 --> 终结thread
   m_thread->wait(100);//(因为是最后的手段,返回值已经不重要了,失败我也没法了)
}

测试使用

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    Controller *controller = new Controller();

    int count = 0;//计数
    QTimer *timer = new QTimer();
    QObject::connect(timer,&QTimer::timeout, controller, [controller,timer,&count](){
        controller->showMessage(QString::number(count++));//[0,10]
        if(count>10){
            timer->stop();
            controller->stopWorkerAsync();
            if(! controller->waitForWorkerDone(100)){
                controller->terminateWorkerAsync();
                //停止失败 --> 强制停止worker
                if(! controller->waitForWorkerTerminated(10)){
                    //强停失败 --> 终结线程执行(最后的手段)
                    controller->terminateThread();
                }
            }
        }
    });
    timer->start(600);

    controller->startWorkerAsync();

    //delete controller;
    return a.exec();
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值