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."的運行錯誤。
http://blog.csdn.net/mznewfacer/article/details/6966752