2

最近、私は狡猾な計画を考えました(tm :P)) プログラムの設定構造を更新する必要があります (15 秒ごとに言ってみましょう)。設定構造は複数の関数によって使用され、それらの関数はすべて複数のスレッドによって呼び出されます。そのため、古い設定構造体を安全に解放できるタイミングを知るために、参照カウンターが必要です。それで、これは正しい方法ですか?コードを注意深く読まなくても大丈夫だという回答はしないでください。共有ポインタに関しては、このような悪用を行うと間違いを犯しやすくなります (私を信じてください)。編集:重要な部分について言及するのを忘れていました。updateSettings() で初期化し、再度呼び出されるまでドロップしないため (その後、myFucntion がメモリ内の 2 つの設定のもう一方を使用するため)、この実装により ref カウンターが 0 にドロップされるのを防ぐことができると思います。

#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();
    }
}
4

1 に答える 1

2

私はこのコードを信用しません。2つの参照カウントを除いて、異なるスレッドによって共有されるすべてのメモリに適切なメモリバリアが欠けていると思います。

これは私にはshared_mutexの良いアプリケーションのように見えます。

編集:

20.7.2.2 [util.smartptr.shared] / p4によると:

データ競合の存在を判断するために、メンバー関数は、shared_ptrオブジェクトとweak_ptrオブジェクト自体にのみアクセスして変更し、参照するオブジェクトにはアクセスしないものとします。

ただし、shared_mutexを使用する代わりに、20.7.2.5shared_ptrアトミックアクセス[util.smartptr.shared.atomic]でAPIを使用することもできます。

複数のスレッドからshared_ptrオブジェクトへの同時アクセスは、アクセスがこのセクションの関数を介して排他的に行われ、インスタンスが最初の引数として渡される場合、データ競合を引き起こしません。

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は、正しく理解するのが簡単になります。ただし、アトミック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
}
于 2011-04-15T13:04:48.730 に答える