2016-04-05 6 views
0

私は非常に似質問は、私は、コンテナスレッドセーフaswellにアクセスしたいではなく、参照することにより、キャッシュされたバージョンを返すと思い、2012年、すでにクリティカルセクションと戻り値

Critical Sections and return values in C++

を求めています。

struct Container { 
    const Data& getSomeData() const { 
    EnterCriticalSection(& myCritSec); 
    if (update) { 
     cache.calulatefromcontainer();   
    } 
    // fill retobj with data from structure 
    LeaveCriticalSection(& myCritSec); 
    return cache; 
    } 

private: 
    mutable Data cache; 
}; 

問題は、「リターンキャッシュ」の行がもう保護されないということです。参照で "キャッシュ"スレッドを安全に戻すことは可能ですか?

+2

変更可能なデータへの参照を戻すことは、基本的にはスレッドセーフなコンテナでは不可能です。ここで捨てるほどではない。 – SergeyA

答えて

0

あなたのクリティカルセクションが実際に保護していることを考える必要があります。

コードでは、myCritSecがコンテナを保護しているようです。しかし、とりわけ、ではなく、cacheというメンバ変数を保護するのはです。これはreturn cache;という行ではなく、参照を返しているためで、他のスレッドがgetSomeData()を呼び出して変更している間に、クライアントコードによって無制限に使用できます。

解決策の1つは、データのコピーを返すことです。

もう1つの解決策は、Dataから情報を取得するために使用できるすべてのパブリック関数が、何らかの形で親コンテナのmyCritSecを使用することです。このアプローチの問題は、レースに落ち込むのは非常に簡単だということです。 getA()getB()の呼び出しは、各CSをロック及びアンロックされているので

const Data &data = container.getSomeData(); 
if (data.getA() == data.getB()) // <--- RACE!!! 
{ 
} 

、別のスレッドがわずかの間でデータを変更して作成することができます:

ユーザコードで
class Data 
{ 
public: 
    int getA() const 
    { 
     int res; 
     EnterCriticalSection(parentCS); 
     res = getAunlocked(); 
     LeaveCriticalSection(parentCS); 
     return res; 
    } 
    int getB() const 
    { 
     int res; 
     EnterCriticalSection(parentCS); 
     res = getBunlocked(); 
     LeaveCriticalSection(parentCS); 
     return res; 
    } 
}; 

そして:例えば競合状態。

+0

本当に安全な唯一のソリューションはデータのコピーですか? –

+0

@vcduser:「唯一の本当の安全ソリューション」?物事を正しく行う方法はたくさんあります。たとえば、コンテナのユーザにCSハンドリングコードを追加することができます(CSハンドリングコードを信頼できる場合など)。または、データのサブセットをコピーすることも、コピーオンライト技術、共有ポインタを使用することもできます。 – rodrigo