Qt 多线程学习

    最近的项目上用到了关于多线程的知识,自己也比较感兴趣,所以就拿了那本《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;
};

    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()。

posted @ 2014-02-11 13:35  SF-_-  阅读(5642)  评论(0编辑  收藏  举报