QMutex類提供的是線程之間的訪問順序化。
QMutex的目的是保護一個對象、資料結構或者程式碼片段,所以同一時間只有一個線程可以訪問它。(在Java術語中,它和同步關鍵字“synchronized”很相似)。例如,這裡有一個方法列印給使用者兩條訊息:
void someMethod() { qDebug("Hello"); qDebug("World"); }
如果同時在兩個線程中調用這個方法,結果的順序將是:
Hello Hello World World
如果你使用了一個互斥量:
QMutex mutex; void someMethod() { mutex.lock(); qDebug("Hello"); qDebug("World"); mutex.unlock(); }
用Java的術語,這段代碼應該是:
void someMethod() { synchronized { qDebug("Hello"); qDebug("World"); } }
然後同一時間只有一個線程可以運行someMethod並且訊息的順序也一直是正確的。當然,這隻是一個很簡單的例子,但是它適用於任何需要按特定頻率發生的情況。
但你在一個線程中調用lock(),其它線程將會在同一地點試圖調用lock()來阻塞,知道這個線程調用unlock()之後其它線程才會獲得這個鎖。lock()的一種非阻塞選擇是tryLock()。
實驗部分:
情形一:
#include <QtCore/QCoreApplication>#include <Qthread>#include <QTextStream>class MyThreadA : public QThread { public: virtual void run(); }; class MyThreadB: public QThread { public: virtual void run(); };int number=6;void MyThreadA::run(){ number *= 5;number /= 4;} void MyThreadB::run(){number *= 3;number /= 2;} int main(int argc, char *argv[]){ QCoreApplication app(argc, argv);MyThreadA a; MyThreadB b; a.run();b.run(); a.terminate();b.terminate();QTextStream out(stdout);out<<number;return app.exec();}
上述代碼,很簡單,寫了兩個線程,覆蓋了QThread的純虛函數run(),這兩個重構的run方法都是對全域變數number的操作,
主函數中順序調用這兩個方法,a.run()執行後number為7,b.run()執行後為10。
情形二:
#include <QtCore/QCoreApplication>#include <Qthread>#include <QTextStream>class MyThreadA : public QThread { public: virtual void run(); }; class MyThreadB: public QThread { public: virtual void run(); };int number=6;void MyThreadA::run(){ number *= 5;sleep(1);number /= 4;} void MyThreadB::run(){number *= 3;sleep(1);number /= 2;} int main(int argc, char *argv[]){ QCoreApplication app(argc, argv);MyThreadA a; MyThreadB b; a.start();b.start(); a.wait(); b.wait(); QTextStream out(stdout);out<<number;return app.exec();}
運行結果:
number=11;
利用QThread的方法start()同是開啟兩個線程,值得注意的是wait()函數,不能等待自己,這個是用來多個線程互動的,所以不能當sleep()用。這個函數是在主線程中被調用的時候阻塞了主線程。如果想在外部讓子線程暫停,最好的辦法是在子線程中設定一個標誌,在主線程中更改這個標誌,並在子線程的run函數中判斷,通過調用其保護函數sleep()來達到暫停目的了。
查看原始碼,即可有清楚的概念:
bool QThread::wait(unsigned long time){ Q_D(QThread); QMutexLocker locker(&d->mutex); if (d->id == GetCurrentThreadId()) { qWarning("QThread::wait: Thread tried to wait on itself"); //當是自身時,直接返回false return false; } if (d->finished || !d->running) //與這個線程對象關聯的線程已經結束執行(例如從run函數返回)。如果線程結束返回真值。如果線程還沒有開始也返回真值。 return true; ++d->waiters; locker.mutex()->unlock(); bool ret = false; switch (WaitForSingleObject(d->handle, time)) { //調用win的對象處理函數 case WAIT_OBJECT_0: //核心對象被啟用,等待成功 ret = true; break; case WAIT_FAILED: qErrnoWarning("QThread::wait: Thread wait failure"); break; case WAIT_ABANDONED: case WAIT_TIMEOUT: default: break; } locker.mutex()->lock(); --d->waiters; if (ret && !d->finished) { //雖然響應成功,但關聯對象未結束執行 // thread was terminated by someone else d->terminated = true; QThreadPrivate::finish(this, false); } if (d->finished && !d->waiters) { //關聯對象執行結束,並且等待數為零時,關閉控制代碼。 CloseHandle(d->handle); d->handle = 0; } return ret;}
情形三:(Mutex 作用)
#include <QtCore/QCoreApplication>#include <Qthread>#include <QTextStream>#include <QMutex>class MyThreadA : public QThread { public: virtual void run(); }; class MyThreadB: public QThread { public: virtual void run(); };QMutex mutex;int number=6;void MyThreadA::run(){ mutex.lock();number *= 5;sleep(1);number /= 4;mutex.unlock();} void MyThreadB::run(){mutex.lock();number *= 3;sleep(1);number /= 2;mutex.unlock();} int main(int argc, char *argv[]){ QCoreApplication app(argc, argv);MyThreadA a; MyThreadB b; a.start();b.start(); a.wait(); b.wait(); QTextStream out(stdout);out<<number;return app.exec();}
運行結果:
number=10;
通過實驗結果可以看出,QMutex保護了全域變數,同一時間只有一個線程可以訪問它。
只得一提的是tryLock()的使用,若以上代碼換為mutex.tryLock();那麼執行結果可能為11,因為是試圖鎖定互斥量。如果鎖被得到,這個函數返回真。如果另一個進程已經鎖定了這個互斥量,這個函數返回假,而不是一直等到這個鎖可用為止。
且不能添上sleep()函數,否則提示 "A mutex must be unlocked in the same thread that locked it."的運行錯誤。