Qt 多线程学习

来源:互联网 发布:handler源码解析 编辑:程序博客网 时间:2024/06/11 05:47

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

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

 

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

复制代码
class Thread : public QThread{    Q_OBJECTpublic:    Thread(QString message = "", QObject *parent = NULL);    ~Thread();    void setMessage(QString);    QString getMessage();    void stop();protected:    void run();private:    QString message;    volatile bool stopped;};
复制代码

    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;}
复制代码

    初始化时将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")) );    }}
复制代码

    在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;}
复制代码

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

0 0
原创粉丝点击