2013-12-22 25 views
3

我的Qt4代碼使用了一些QThread實例,它們在包含一些QString字段的公共數據結構上運行。它可以歸結爲以下幾點:Qt4 C++:QString多線程崩潰的變量用法

我的數據結構:

class My : public QObject{ 
    Q_OBJECT 
public: 
    QString foo; 
}; 

的線程中執行:

class Thr : public QThread{ 
public: 
    My* my; 
protected: 
    void run(){ 
     while (true){ 
      QString copy = my->foo; 
      QString bar = copy.toUpper(); 
      my->foo = bar.toLower(); 
     } 
    } 
}; 

這是我對這個問題的研究編寫的測試應用程序。當然,它沒有做任何實際有用的:)

如果我初始化My的一個實例並啓動一個線程與該實例,一切都很好。但是當我使用相同的My實例啓動第二個實例時,它會看到不同的消息,看起來像堆/堆棧/不管有什麼損壞。

這是正常的嗎?我一般都知道多線程問題,也知道Qt的QMutex可以避免這個問題。但據我正確理解Qt文檔,我被允許以這種方式使用它。我不同時操作同一個QString實例(可能是因爲一些奇怪的隱式共享機制 - 但文檔聲明這對用戶來說是完全透明的!)。如上所述,我的問題不在於如何重寫代碼,而是從「開始於Qt 4」開始,隱式共享類可以安全地跨線程複製,就像任何其他值類一樣,它們是完全可重入的。隱式共享是隱含的。「 (http://qt-project.org/doc/qt-4.8/threads-modules.html)我誤解了。

+1

同時訪問同一對象,至少有一個如果是修改,則創建一個* data race *。具有數據競賽的程序具有未定義的行爲。在我看來,你的幾個線程正在同時修改(通過賦值)'my-> foo'('my-> foo = bar.toLower();')。 – Casey

+0

好的,那麼在賦值中,'foo'實例可能處於不一致的'inbetween'狀態?我的確希望,至少使用這些非常基本的Qt類型的賦值本身是一個線程安全的操作,所以我可以同時訪問它,並且我可以獲得舊值或新值,但不會崩潰:)所以,我已經使用指針或互斥鎖,這並不能使代碼更具可讀性...... – ginger

+0

C++不保證指針賦值是原子的;)任何你不包裹在「std :: atomic 」中的東西都需要被保護以防數據競爭。 – Casey

回答

0

正如在評論中指出的,你是可能是試圖從不同的線程寫入相同的數據。我寫「可能」只是因爲你沒有分享QThread子類的用法。

正如在評論中指出的那樣,甚至連C++標準都沒有保證賦值是線程安全的,例如Object子類中的QString

根據提示,您可以使用std::atomic,儘管它自C++ 11以來纔可用。更爲複雜的解決方案是將QMutex用於您的場景,或者可能更好地使用與QMutexLocker一起使用的RAII解決方案,該解決方案將自動處理您的解鎖。所以你會像下面的代碼一樣。

class Thr : public QThread{ 
public: 
    My* my; 
    QMutex mutex; // <--- first addition 
protected: 
    void run(){ 
     while (true){ 
      QString copy = my->foo; 
      QString bar = copy.toUpper(); 
      QMutexLocker locker(&mutex); // <--- second addition 
      my->foo = bar.toLower(); 
     } 
    } 
}; 

當然,推薦的解決方案取決於其它因素,例如,你也可以重新設計你的程序不使用取決於具體的使用情況下的手,等一個指針。