執行緒同步(QMutex)

如果一個物件所持有的資料,可以被多個執行緒同時寫入和讀取時,必須考慮到資料同步的問題,像假如我們有以下的類別:

class Counter{
private:
    int n ;
public:
    counter() { n=0;}
    void increment() {n++;}
    void decrement() {n--;}
    int getValue() {return n;}
};

這個類別不是線程安全的,如果讓多個線程修改成員變數n,結果是不可預測的,因為++和–不是原子操作,它們會被分成3到機器指令,第一條指令向暫存器存入變數的值,第二條指令遞增或遞減暫存器的值,第三條指令將暫存器的值存回變數。如果有兩個線程同時加載變數的值,更改後再存回,結果可能相互覆蓋,就像是如果變數n原本為0,有兩個執行緒同時進行遞增操作,可能因為覆蓋使得結果為1,而不是預期的2。

為了避免這種情況,我們可以加上QMutex,當執行緒執行到QMutex的lock()時,此時會鎖定接下來的程式流程,其它嘗試執行此流程的執行緒必須等待,等到此執行緒執行到QMutex的unlock()後,才可以進行,我們對之前的類別進行改寫,使得此類別的遞增和遞減在多執行緒操作下是安全的。

class Counter {
private:
    QMutex mutex;
    int n ;
public:
    counter() { n=0;}
    void increment() {mutex.lock(); n++; mutex.unlock();}
    void decrement() {mutex.lock(); n--; mutex.unlock();}
    int getValue() {return n;}
};

也可以使用QMutexLocker,建構時以QMutex物件作為引數並進行鎖定,QMutexLocker解構時自動解除鎖定,兩者效果相同,以下為改寫的遞增遞減函式。

class Counter {
private:
    QMutex mutex;
    int n ;
public:
    counter() { n=0;}
    void increment() {QMutexLocker locker(&mutex); n++; }
    void decrement() { QMutexLocker locker(&mutex); n--;}
    int getValue() {return n;}
};