2016-10-03 5 views
0

私はスレッドセーフなshared_ptrでクラスを作成しようとしています。私の使用例は、shared_ptrがクラスのオブジェクトに属し、シングルトンのような振る舞いをする(CreateIfNotExist関数は、任意の時点で任意のスレッドで実行できます)。ロックなしでthread_safe shared_ptrを作成する正しい方法はありますか?

ポインタがnullの場合、値を設定する最初のスレッドが勝ち、同時にそのスレッドを作成している他のすべてのスレッドが勝ちスレッドの値を使用します。ここで

は、私がこれまで(残りはテスト目的であり、問​​題の唯一の機能はCreateIfNotExist()関数であることに注意してください)したものである:

#include <memory> 
#include <iostream> 
#include <thread> 
#include <vector> 
#include <mutex> 

struct A { 
    A(int a) : x(a) {} 
    int x; 
}; 

struct B { 
    B() : test(nullptr) {} 

    void CreateIfNotExist(int val) { 
     std::shared_ptr<A> newPtr = std::make_shared<A>(val); 
     std::shared_ptr<A> _null = nullptr; 
     std::atomic_compare_exchange_strong(&test, &_null, newPtr); 
    } 

    std::shared_ptr<A> test; 
}; 

int gRet = -1; 
std::mutex m; 

void Func(B* b, int val) { 
    b->CreateIfNotExist(val); 
    int ret = b->test->x; 

    if(gRet == -1) { 
     std::unique_lock<std::mutex> l(m); 
     if(gRet == -1) { 
      gRet = ret; 
     } 
    } 

    if(ret != gRet) { 
     std::cout << " FAILED " << std::endl; 
    } 
} 

int main() { 
    B b; 

    std::vector<std::thread> threads; 
    for(int i = 0; i < 10000; ++i) { 
     threads.clear(); 
     for(int i = 0; i < 8; ++i) threads.emplace_back(&Func, &b, i); 
     for(int i = 0; i < 8; ++i) threads[i].join(); 
    } 
} 

が、これは、これを行うための正しい方法は何ですか? CreateIfNotExist()を同時に呼び出すすべてのスレッドがすべて同じshared_ptrを使用するようにするには、より良い方法がありますか?おそらく、これらの線に沿って

+0

test'が有効である必要があり、 'ので、 'B'がcontructableデフォルトではないことはありませなぜ? – NathanOliver

+0

ただ1つの共有ポインタを定義し、それをスポーンするごとに各スレッドに渡します。 – sji

+0

@ sji私は、私のユースケースに合うような方法でコードを意図的に構造化しましたが、多くのリファクタリングがなければそれはできません。 – Andrew

答えて

4

何か:

struct B { 
    void CreateIfNotExist(int val) { 
    std::call_once(test_init, 
        [this, val](){test = std::make_shared<A>(val);}); 
    } 

    std::shared_ptr<A> test; 
    std::once_flag test_init; 
}; 
+0

このアプローチはどの程度軽量ですか?比較とスワップの方が効率的でしょうか? – Andrew

+1

あなたのユースケースによって異なります。 'once_flag'のために構造体を大きくしますが、構造体が1つだけ作成されることを保証します。一方、質問からのアプローチは構造体を膨らませませんが、 'n'のほぼ同時の構造を持つことができ、それらのうちの1つだけをすべて破棄します。どちらが良いかはあなた次第です。 – krzaq

+0

shared_ptrを任意の時点でnullptrに設定できるという要件もありますが、このアプローチは機能しますか? – Andrew