2011-04-15 12 views
2

最近私は狡猾な計画(tm:P)を考えました 私のプログラムで設定の構造を更新しなければなりません。設定構造体は複数の関数によって使用され、それらの関数はすべて複数のスレッドによって呼び出されます。 古い設定の構造体を解放するのがいつ安全であるかを知るために、参照カウンタが必要です。 これは正しい方法ですか? コードを慎重に読んでいない場合は、共有ポインターに関してはこのような悪用を行うと間違いを犯すのは簡単です(信頼してください)。 編集:重要な部分については言及しません。私はこの実装がrefSetを0に落とさないようにしていると思います。なぜなら、私はupdateSettings()で初期化し、再度呼び出されるまで落とされないからです(そして、myFucntionはメモリの2つの設定のうちのもう1つを使います)。(Ab)shared_ptrを参照カウンタとして使用

#include<memory> 
#include <cstdio> 
#include <iostream> 
#include <vector> 
using namespace std; 
struct STNGS 
{ 
    int i; 
    vector<double> v; 
}; 
static int CUR_STNG=0; 
shared_ptr<STNGS> stngsArray[2]; 
int myFunction() //called by multiple threads 
{ 
    shared_ptr<STNGS> pStngs=stngsArray[CUR_STNG]; 
    STNGS& stngs=*pStngs; 
    //do some stuff using stngs 

} 

void updateSettings() 
{ 
    auto newIndex=(CUR_STNG+1)%2; 
    stngsArray[newIndex].reset(new STNGS); 
    CUR_STNG=newIndex; 
} 
void initialize() 
{ 
    auto newIndex=CUR_STNG; 
    stngsArray[newIndex].reset(new STNGS); 
    CUR_STNG=newIndex; 
} 
int main() 
{ 
    initialize(); 
    //launch bunch of threads that are calling myFunction 
    while(true) 
    { 
     //call updateSettings every 15 seconds 
    } 
} 

編集:私は、コードを更新し、コメントからのフィードバックを使用して:

#include<memory> 
#include <cstdio> 
#include <iostream> 
#include <vector> 
using namespace std; 
static const int N_STNG_SP=4; 
static int CUR_STNG=0; 
struct STNGS 
{ 
    int i; 
    vector<double> v; 
    STNGS() 
    { 
     for (int i=0;i<10;++i) 
      v.push_back(42); 
    } 
}; 
shared_ptr<STNGS> stngs[N_STNG_SP]; 
int myFunction() //called by multiple threads 
{ 
    shared_ptr<STNGS> pStngs=stngs[CUR_STNG]; 
    STNGS& stngs=*pStngs; 
    //do some stuff using stngs 
} 

void updateSettings() 
{ 
    auto pStng=new STNGS; 
    //fill *pStng 
    int newVer=(CUR_STNG+1)%N_STNG_SP; 
    stngs[newVer].reset(pStng); 
    CUR_STNG=newVer; 
} 
void initialize() 
{ 
    auto pStng=new STNGS; 
    //fill *pStng 
    int newVer=(CUR_STNG+1)%N_STNG_SP; 
    stngs[newVer].reset(pStng); 
    CUR_STNG=newVer; 
} 
int main() 
{ 
    initialize(); 
    //launch bunch of threads that are calling myFunction 
    while(true) 
    { 
     //call updateSettings every 15 seconds 
     updateSettings(); 
    } 
} 

答えて

2

私はこのコードを信頼しないでしょう。私はそれが2つの参照カウントを除いて、異なるスレッドによって共有されるすべてのメモリ上の適切なメモリ障壁が欠けていると信じています。

これは私にとってshared_mutexの良いアプリケーションのようです。

編集:

20.7.2.2 [util.smartptr.shared]/P4は言う:データ競合の 存在を決定する目的のため

、部材 機能がアクセスしなければなりません shared_ptrオブジェクトとweak_ptrオブジェクトだけを変更してください。 オブジェクトは参照するオブジェクトではなく、 を参照してください。 shared_ptrのに

同時アクセス:

しかし、代わりにshared_mutexを使用するのではなく、別のオプションは、20.7.2.5のshared_ptrアトミックアクセス[util.smartptr.shared.atomic]でAPIを使用するのが良いかもしれません 複数のスレッドのオブジェクトが のアクセス権を持つ場合は、 のデータ競合を導入し、そのインスタンスは の最初の引数として渡されます。

template<class T> 
    bool atomic_is_lock_free(const shared_ptr<T>* p); 
template<class T> 
    shared_ptr<T> atomic_load(const shared_ptr<T>* p); 
template<class T> 
    shared_ptr<T> atomic_load_explicit(const shared_ptr<T>* p, memory_order mo); 
template<class T> 
    void atomic_store(shared_ptr<T>* p, shared_ptr<T> r); 
template<class T> 
    void atomic_store_explicit(shared_ptr<T>* p, shared_ptr<T> r, memory_order mo); 
template<class T> 
    shared_ptr<T> atomic_exchange(shared_ptr<T>* p, shared_ptr<T> r); 
template<class T> 
    shared_ptr<T> 
    atomic_exchange_explicit(shared_ptr<T>* p, shared_ptr<T> r, memory_order mo); 
template<class T> 
    bool 
    atomic_compare_exchange_weak(shared_ptr<T>* p, shared_ptr<T>* v, shared_ptr<T> w); 
template<class T> 
    bool 
    atomic_compare_exchange_strong(shared_ptr<T>* p, shared_ptr<T>* v, shared_ptr<T> w); 
template<class T> 
    bool 
    atomic_compare_exchange_weak_explicit(shared_ptr<T>* p, shared_ptr<T>* v, 
              shared_ptr<T> w, memory_order success, 
              memory_order failure); 
template<class T> 
    bool 
    atomic_compare_exchange_strong_explicit(shared_ptr<T>* p, shared_ptr<T>* v, 
              shared_ptr<T> w, memory_order success, 
              memory_order failure); 

shared_mutexは、権利を取得することが容易になります。しかし、atomic shared_ptr APIは、より高いパフォーマンスのソリューションをもたらすかもしれません。

アップデート:ここで

がshared_mutexソリューションのためのテストされていないコードは(ノートshared_mutexがSTDではなく、サードパーティのライブラリである)である:ここでは

struct STNGS 
{ 
    int i; 
    vector<double> v; 
    ting::shared_mutex m; 
}; 

STNGS stngs; 

int myFunction() //called by multiple threads 
{ 
    shared_lock<shared_mutex> _(stngs.m); 
    //do some stuff using stngs 
    return 0; 
} 

void updateSettings() 
{ 
    unique_lock<shared_mutex> _(stngs.m); 
    //fill stngs 
} 

void initialize() 
{ 
    //fill stngs 
} 

は原子の負荷を使用してテストされていないコードであります/ shared_ptrの関数を格納する:

struct STNGS 
{ 
    int i; 
    vector<double> v; 
}; 

shared_ptr<STNGS> pStng; 

int myFunction() //called by multiple threads 
{ 
    shared_ptr<STNGS> stngs = atomic_load(&pStng); 
    //do some stuff using *stngs 
    return 0; 
} 

void updateSettings() 
{ 
    shared_ptr<STNGS> newStng(new STNGS); 
    //fill *newStng 
    atomic_store(&pStng, newStng); 
} 

void initialize() 
{ 
    pStng.reset(new STNGS); 
    //fill *pStng 
} 
+1

ああ、もう一度答えは消えました...しかし、もう一度聞いてください。なぜatomic_loadが必要ですか?私は、アトミックストアが必要であることを理解していますが、atomic_loadはatomic_loadを保証していませんか?つまり、変数はロックされていますか?そして、私は他の機能は設定を読み込むように言った。それが愚かなQなら私にいくつかのリンクを教えてください。愚かなQといえば、生のポインタがコンストラクタの引数として使用されている場合、共有されたptrが値をコピーしないことがわかりました。それは新しい(:P)私にはまだ、私はまだwheatherについての議論を持っていないこれは、標準によって良い考えであるかどうかです。 – NoSenseEtAl

+2

アトミック命令は、指示された操作(実際にはどのようにブーストの実装がそれを行うか)を包み込んだミューテックス以上のものであると考えてください。各shared_ptrは、参照カウントを更新するだけでなく、一貫して読み書きが必要な2つのポインタで構成されています。 shared_ptrを設定しているスレッドが、同じshared_ptrを読み込む前に設定されているポインタのうちの1つだけを取得すると、問題が発生します。したがって、リーダーとライターの両方が互いに同期しなければなりません。 atomic_loadは、読み込み中にライターが設定しないように、フードの下にあるmutexをロックします。 –

+1

OK、わかりました。あなたは私の "共有ポインタの円形配列"解決策にいくつかの問題を見ますか?私はそれが単に変更されている1つのインデックスであり、それは "かなり原子的"でなければならないので、どのように失敗するかはわかりません。 Ofc関数は長い時間がかかる可能性があり、まだ使用されている間に円形配列(キュー)のメンバーを更新し始めますが、キューのサイズを大きくするとうまくいくはずです。 – NoSenseEtAl

関連する問題