2008-11-07 12 views
1

這個問題是關於在工作線程中使用單例對象的getter方法。下面是一些僞代碼第一:單線程工人方法中的單例getInstance

// Singleton class which contains data 
class MyData 
{ 
    static MyData* sMyData ; 

    int mData1[1024]; 
    int mData2[1024]; 
    int mData3[1024]; 

    MyData* getInstance() 
    { 
     // sMyData is created in the very beginning. 
     return sMyData ; 
    } 

    void getValues(int idx, int& data1,int& data2,int& data3) 
    { 
     data1 = mData1[idx]; 
     data2 = mData2[idx]; 
     data3 = mData3[idx]; 
    } 

    int* getData1() 
    { 
     return &mData1[0]; 
    } 
} 

class MyThread 
{ 
    void workerMethod() 
    { 
     MyData* md = MyData::getInstance(); 

     int d1,d2,d3; 
     md->getValue(12, d1,d2,d3); 

     int* data1 = md->getData1(); 
     d1 = data1[34]; 
    } 
} 

現在,當你看到我有一些getter方法(所有隻讀),邁德特::的getInstance(),邁德特::的getValue()和邁德特:: getData1() 。第一個問題是這些方法的線程安全性如何?

因爲它們通常被稱爲方法,所以使用互斥鎖來保護這些方法是我想要避免的。

第二個問題是:在多線程應用程序中,特別是在工作方法中,從中央源讀取數據的建議方式是什麼。

謝謝!

保羅

回答

5

只要沒有其他線程將嘗試寫在你的單身對象中的數據,你並不需要保護他們:顧名思義,在沒有一個作家的讀者多是線程安全的。這是一個常見的模式,程序的初始化代碼設置一個單例,然後只由工作線程讀取。

但是,如果任何線程曾經寫入這些數據而另一些則從中讀取,你必須以某種方式保護它。如果你有很多的讀者,只有偶爾的作家,這是值得考慮的某種「讀寫」鎖定,它允許多個讀者在沒有任何作家的情況下。

+0

你不需要鎖定,只是因爲witting。如果寫入是非原子的,則只需要一個鎖。整數寫入是原子的。如果某事必須編寫2個整數來保持一致的狀態,那麼您將需要一個鎖來確保在任何人讀取對象之前更新這兩個整數。 – 2008-11-07 14:58:47

+0

詮釋賦值不保證是原子... – user23167 2008-11-07 17:38:55

1

你的方法沒問題,但你有兩個主要問題。

首先,MyData :: getInstance()應該是靜態的,不會創建實例。爲了儘量減少互斥量的使用,你應該檢查出double checked lock。其次,線程的安全性取決於getter方法的調用者而不是MyData類的調用者。如果這就是你想要的,那麼很好。如果沒有,那麼你將不得不在MyClass中想出某種訪問控制。另外,由於你包含的數據類型只是一個基本的類型(int),所以很可能你永遠不會看到任何同步問題,直到你的代碼在生產之後。

2

不可能判斷這是否是線程安全的。如果數據在對象創建過程中被初始化,並且永遠不會改變,否則將會正確運行。如果您通過其他方法改變底層數據,那麼讀者將不得不針對作者進行某種同步,這是沒有辦法的。

根據你在做什麼,你可能能夠使用比互斥量更輕的東西,比如原子更新同步命令,或者使用讀寫器鎖,但不知道你在做什麼更多,這是不可能的告訴。

1

先回去讀了這個問題,以獲得更好的版本singelton的:
Can any one provide me a sample of Singleton in c++?

還要注意:不要使用單作爲一個榮耀的全局變量。
這隻會增加一個糟糕設計的複雜性。只需使用一個全局變量。

只要你只是從單身人士讀取它使用時是線程安全的。

唯一不是線程安全的(不受語言保證的)是在創建過程中。所以從技術上說,你應該在創建實例的部分添加鎖,以保證在任何人都可以使用它之前完全創建單例。

注意:不要被使用雙重檢查鎖定來優化您的鎖定策略。它可以使不是可以在C++中正常工作。閱讀DDJ article

如果您可以保證在單線程環境中(在創建任何線程之前)實例化單例(可能是第一次調用getInstance()),那麼您可以在實例化過程中免除鎖的需要。

如果你改變你的代碼,以便其他線程可以寫入單例,那麼你需要考慮鎖定和一致性。如果你的寫入不是原子的,你只需要鎖定。如果你的寫法只是更新一個整數,它已經是原子的,不需要改變。如果你的代碼是不是原子:

  • 寫多個整數的方法
  • 內執行讀,然後寫

然後,你將需要鎖定的對象,直到所有寫入完成,因此還鎖定具有讀權限的方法以阻止他們從對象讀取數據,而另一個方法更新對象。

因此,這是成員變量只能由成員方法訪問的另一個原因。

0

對於線程安全性,您需要將整個類視爲一個整體。正如你所寫的,你的班級不會是線程安全的。雖然getValues方法沒問題,但getData1方法有問題。

你說他們是(只讀)getter方法。但是,它們都沒有被聲明爲const方法。 getData1作爲一個const方法是無效的,因爲它返回一個非const指針。另外,當你公開你的實現時,返回一個指向私有類數據的指針是不好的。

如果在線程啓動前,這是一個單例類,它在初始化時保存一些基本上靜態的數據集,那麼所有的訪問器都應該是const方法。 getInstance也應該返回一個指向類的const指針(並且是另一個答案提到的靜態方法)。

相關問題