Qt 多線程學習

    最近的項目上用到了關於多線程的知識,本身也比較感興趣,因此就拿了那本《C++ GUI Qt4 編程》來學習。編程

    這本書的第14章是關於多線程的知識,使用的Qt版本是Qt4.x。在下用的是最新的Qt 5.2,因此代碼上有一些不兼容,稍加修改就能夠運行了。多線程

 

    Qt的多線程簡單來講就是繼承QThread類,重載run()函數,start()啓動線程。首先來看下書上的第一個例子:(修改版的代碼已上傳,點擊下載app

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時才退出。ui

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*>類型的。this

    這樣就能夠方便的實現多個線程的修改,以下圖:spa

    

    另外,第四個例子對我也頗有啓發:線程

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

相關文章
相關標籤/搜索