Youtube上有一個很出名的Qt視頻教程,講得簡練精要。但是在他講到的Qt線程同步一集的時候,我憑着自己的經驗,感覺他講得是錯的。於是在網上大範圍的搜索“qt線程同步”這個關鍵字,試圖找到一些線索,以證明視頻教程中的錯誤。但是看了很多個博客之後,我發現大家都是千篇一律,很是吃驚。真是誤導大家。所以特意寫這篇文章來證實一下。
首先我們要知道爲什麼要用線程同步?那是因爲在多線程編程裏面,會有一些敏感數據不允許被多個線程同時訪問,此時就使用同步訪問技術,保證數據在任何時刻,最多有一個線程訪問,以保證數據的完整性。
在這裏我們引用一個網上較爲流行的一個例子。如下所示,這是一個線程的實現:
class Thread : public QThread
{
public:
Thread();
void stop();
protected:
virtual void run();
private:
bool m_stop;
};
Thread::Thread()
{
m_stop = false;
}
void Thread::stop()
{
m_stop = true;
}
void Thread::run()
{
while (!m_stop)
{
sleep(1);
qDebug("vic.MINg!");
}
qDebug("end!");
}
Qt中的線程是以對象的形式存在的。如果我們在main函數中生成幾個此類的線程對象,如下:
Thread m1;
Thread m2;
Thread m3;
m1.start();
m2.start();
m3.start();
那麼m1,m2,m3之間會有我們自己定義的共享數據存在嗎?顯然,從C++對象的概念出發來理解,他們之間是不會存在這些共享數據的。因爲各個對象會維護各自對象空間裏的變量。按上面例子中的代碼來看,m1,m2,m3分別有着一個自己的m_stop,那我們還有必要對這個m_stop來做同步操作嗎?顯然是沒有必要的。而網上的多數例子,以及那個Youtube的視頻卻對m_stop做了同步操作,引用原文的代碼:
//thread.h頭文件,添加互斥量對象
private:
...
QMutex mutex;
};
void Thread::run()
{
forever {
mutex.lock();
if (m_stop) {
m_stop = false;
mutex.unlock();
break;
}
mutex.unlock();
qDebug("vic.MINg!");
}
qDebug("end!");
}
void Thread::stop()
{
mutex.lock();
m_stop = true;
mutex.unlock();
}
文中的意思是使用QMutex保護上面的線程類的m_stop布爾變量,我們就納悶了,一個不被多個線程共享的數據還需要被保護呢?其中一個線程對象把這個變量改變了,其他線程對象的這個變量又不會受到什麼影響。這樣做又有什麼意義可言。
且不爭論這個,這還不算最大的問題所在。最大的問題是定義的互斥變量也是作爲類的成員變量來定義的。那麼由這個線程類生成m1,m2,m3對象勢必是三個不相干的互斥量,你怎麼可能因爲把m1的互斥量上鎖,而不能讓m2對自己的互斥量上鎖呢?也就是說,這三個線程壓根不是被同一個互斥量來協調,以達到同步的。因此,網上的多數Qt多線程同步的例子可以說是錯誤的。
線程同步,首先你必須保證多個線程受控於共有的一個互斥量,我們想達到共有一個互斥量需要將互斥量定義爲靜態變量纔對。如下所示:
class Thread : public QThread
{
public:
Thread();
~Thread();
private:
void run();
public:
QString name;
static QMutex m;//只有這種情況下,互斥量纔是被所有此類線程所共享的
};
//這一句最好定義在對應的cpp文件中,避免重複定義
QMutex MyThread::m; //靜態對象必須定義
由C++對象的特徵我們可以知道,以上的QMutex對象在所有的此類線程中是共享的,通過這樣去修改,才真正地多個線程同步。爲了避免大家被網上錯誤的講解所誤解,免得把多線程同步按照他們的方式去寫,特發此文。
如果哪裏有不對,請大家給我指出,我將不勝感激!!
我把隨便參考的一個錯誤鏈接貼出來:http://mobile.51cto.com/symbian-272643.htm
Youtube視頻鏈接:http://www.youtube.com/playlist?list=PL2D1942A4688E9D63