• [转] Qt 多线程学习


    Qt 多线程学习

    转自:http://www.cnblogs.com/IT-BOY/p/3544220.html

    最近的项目上用到了关于多线程的知识,自己也比较感兴趣,所以就拿了那本《C++ GUI Qt4 编程》来学习。

    这本书的第14章是关于多线程的知识,使用的Qt版本是Qt4.x。在下用的是最新的Qt 5.2,所以代码上有一些不兼容,稍加修改就可以运行了。

    Qt的多线程简单来说就是继承QThread类,重载run()函数,start()启动线程。首先来看下书上的第一个例子:(修改版的代码已上传,点击下载

    class Thread : public QThread
    {
        Q_OBJECT
    public:
        Thread(QString message = "", QObject *parent = NULL);
        ~Thread();
        void setMessage(QString);
        QString getMessage();
    
        void stop();
    
    protected:
        void run();
    
    private:
        QString message;
        volatile bool stopped;
    };
    View Code

    Thread类继承了QThread类,并实现了run函数。stopped变量前面的volatile声明stopped为易失性变量,这样每次读取stopped时都是最新的值。

    继续看Thread类的实现:

    Thread::Thread(QString message, QObject *parent) :
        stopped(false)
      , QThread(parent)
      , message(message)
    {
    }
    
    Thread::~Thread()
    {
        this->stop();
        this->wait();
        qDebug() << this;
    }
    
    void Thread::setMessage(QString message)
    {
        this->message = message;
    }
    
    QString Thread::getMessage()
    {
        return this->message;
    }
    
    void Thread::stop()
    {
        stopped = true;
    }
    
    void Thread::run()
    {
        while (!stopped)
            std::cerr << qPrintable(message);
        stopped = false;
        std::cerr << std::endl;
    }
    View Code

    初始化时将stopped设置为false,run函数中持续检查stopped的值,为true时才退出。

    Dialog::Dialog(QWidget *parent)
        : QDialog(parent)
    {
        QPushButton *buttonQuit = new QPushButton(QString::fromLocal8Bit("Quit"));
        connect(buttonQuit, &QPushButton::clicked, this, &Dialog::close);
    
        QBoxLayout *layout = new QBoxLayout(QBoxLayout::LeftToRight, this);
    
        QStringList list = QString("ABCDEFGHIJKLMN").split("",QString::SkipEmptyParts);
    
        foreach (QString name, list)
        {
            Thread *thread = new Thread(name, this);
            QPushButton *button = new QPushButton(QString("Start ")+name, this);
            mappingTable.insert(button, thread);
            connect(button, &QPushButton::clicked, this, &Dialog::startOrStopThread);
            layout->addWidget(button);
        }
    
        layout->addWidget(buttonQuit);
        this->setLayout(layout);
    }
    
    void Dialog::startOrStopThread()
    {
        QPushButton *buttonNow = dynamic_cast<QPushButton*>(sender());
        Thread *threadNow = (Thread*)mappingTable[buttonNow];
    
        if (threadNow == NULL) return;
    
        if(threadNow->isRunning())
        {
            threadNow->stop();
            buttonNow->setText( buttonNow->text().replace(QString("Stop"),QString("Start")) );
        }
        else
        {
            threadNow->start();
            buttonNow->setText( buttonNow->text().replace(QString("Start"),QString("Stop")) );
        }
    }
    View Code

    在Dialog界面类中,将button与thread实现一一对应的连接,在槽函数中就可以方便的找到对应的线程了。其中mappingTable是QMap<QObject*, QObject*>类型的。

    这样就可以方便的实现多个线程的修改,如下图:

        

    另外,第四个例子对我也很有启发:

    TransactionThread::TransactionThread(QObject *parent) :
        QThread(parent)
    {
        start();
    }
    
    TransactionThread::~TransactionThread()
    {
        {
            QMutexLocker locker(&mutex);
    
            while (!transactions.isEmpty())
                delete transactions.dequeue();
    
            transactionCondition.wakeOne();
        }
    
        wait();
    }
    
    void TransactionThread::addTransaction(Transaction *transaction)
    {
        QMutexLocker locker(&mutex);
        transactions.enqueue(transaction);
        transactionCondition.wakeOne();
    }
    
    void TransactionThread::run()
    {
        Transaction *transaction = 0;
        QImage oldImage;
    
        forever
        {
            {
                QMutexLocker locker(&mutex);
    
                if (transactions.isEmpty())
                    transactionCondition.wait(&mutex);
    
                if (transactions.isEmpty())
                    break;
    
                transaction = transactions.dequeue();
                oldImage = currentImage;
            }
    
            emit transactionStarted(transaction->message(), 0);
            QImage newImage = transaction->apply(oldImage);
            delete transaction;
    
            {
                QMutexLocker locker(&mutex);
                currentImage = newImage;
    
                if (transactions.isEmpty())
                    emit allTransactionsDone();
            }
        }
    }
    
    void TransactionThread::setImage(const QImage& image)
    {
        QMutexLocker locker(&mutex);
        currentImage = image;
    }
    
    QImage TransactionThread::getImage()
    {
        QMutexLocker locker(&mutex);
        return currentImage;
    }
    View Code

    以上为线程实现的关键代码。在读取和写入从线程与主线程共享的变量时,都要使用mutex互斥变量。使用QMutexLocker locker(&mutex)也更方便,在构造是lock,析构时unlock,临时变量超过了作用域自然被析构,不得不说实现者的方法很巧妙啊。至于transactionCondition.wait(&mutex)则是等待条件。当事务队列为空时,等待事务加入,或者析构。加入事务时唤醒即可,即transactionCondition.wakeOne()。

  • 相关阅读:
    Spring中Bean及@Bean的理解
    Visual Studio(VS)秘钥集合
    PLC不能初始化问题
    【原创】C# API 未能创建 SSL/TLS 安全通道 问题解决
    【原创】XAF CriteriaOperator 使用方式汇总
    【原创】.Net WebForm Calendar 日历控件常用方法
    【原创】XAF常用属性字段设置
    【记录】Windows 操作系统常用快捷命令
    【XAF】常见错误以及对应解决方法
    【原创】XAF ITreeNode+NonPersistent 使用方式
  • 原文地址:https://www.cnblogs.com/hate13/p/4633974.html
Copyright © 2020-2023  润新知