QMutex類提供的是線程之間的訪問順序化。
QMutex的目的是保護一個對象、數(shù)據(jù)結構或者代碼段,所以同一時間只有一個線程可以訪問它。(在Java術語中,它和同步關鍵字“synchronized”很相似)。例如,這里有一個方法打印給用戶兩條消息:
[cpp]
view plaincopyvoid someMethod()
{
qDebug("Hello");
qDebug("World");
}
如果同時在兩個線程中調用這個方法,結果的順序將是:
Hello Hello World World 如果你使用了一個互斥量:
[cpp]
view plaincopyQMutex mutex;
void someMethod()
{
mutex.lock();
qDebug("Hello");
qDebug("World");
mutex.unlock();
}
用Java的術語,這段代碼應該是:
[cpp]
view plaincopyvoid someMethod()
{
synchronized {
qDebug("Hello");
qDebug("World");
}
}
然后同一時間只有一個線程可以運行someMethod并且消息的順序也一直是正確的。當然,這只是一個很簡單的例子,但是它適用于任何需要按特定頻率發(fā)生的情況。
但你在一個線程中調用lock(),其它線程將會在同一地點試圖調用lock()來阻塞,知道這個線程調用unlock()之后其它線程才會獲得這個鎖。lock()的一種非阻塞選擇是tryLock()。
實驗部分:
情形一:
[cpp]
view plaincopy#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的純虛函數(shù)run(),這兩個重構的run方法都是對全局變量number的操作,
主函數(shù)中順序調用這兩個方法,a.run()執(zhí)行后number為7,b.run()執(zhí)行后為10。
情形二:
[cpp]
view plaincopy#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()函數(shù),不能等待自己,這個是用來多個線程交互的,所以不能當sleep()用。這個函數(shù)是在主線程中被調用的時候阻塞了主線程。如果想在外部讓子線程暫停,最好的辦法是在子線程中設置一個標志,在主線程中更改這個標志,并在子線程的run函數(shù)中判斷,通過調用其保護函數(shù)sleep()來達到暫停的目的了。
查看源代碼,即可有清楚的概念:
[cpp]
view plaincopybool 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) //與這個線程對象關聯(lián)的線程已經(jīng)結束執(zhí)行(例如從run函數(shù)返回)。如果線程結束返回真值。如果線程還沒有開始也返回真值。
return true;
++d->waiters;
locker.mutex()->unlock();
bool ret = false;
switch (WaitForSingleObject(d->handle, time)) { //調用win的對象處理函數(shù)
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) { //雖然響應成功,但關聯(lián)對象未結束執(zhí)行
// thread was terminated by someone else
d->terminated = true;
QThreadPrivate::finish(this, false);
}
if (d->finished && !d->waiters) { //關聯(lián)對象執(zhí)行結束,并且等待數(shù)為零時,關閉句柄。
CloseHandle(d->handle);
d->handle = 0;
}
return ret;
}
情形三:(Mutex 作用)
[cpp]
view plaincopy#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();那么執(zhí)行結果可能為11,因為是試圖鎖定互斥量。如果鎖被得到,這個函數(shù)返回真。如果另一個進程已經(jīng)鎖定了這個互斥量,這個函數(shù)返回假,而不是一直等到這個鎖可用為止。
且不能添上sleep()函數(shù),否則提示 "A mutex must be unlocked in the same thread that locked it."的運行錯誤。