(Ab) использование shared_ptr в качестве счетчика ссылок - PullRequest
2 голосов
/ 15 апреля 2011

Недавно я подумал о хитром плане (тм: P)) Я должен обновить структуру настроек в моей программе (скажем, каждые 15 секунд).Структура настроек используется несколькими функциями, и каждая из этих функций вызывается несколькими потоками.Поэтому мне нужен счетчик ссылок, чтобы знать, когда безопасно освобождать старую структуру настроек.Так это правильный способ сделать это?Пожалуйста, не отвечайте, что это нормально, если вы не внимательно прочитали код, когда дело доходит до общих указателей, легко совершать ошибки, когда совершаете подобные злоупотребления (поверьте мне).РЕДАКТИРОВАТЬ: я забыл упомянуть важную часть.Я думаю, что эта реализация предотвращает сброс счетчика ref до 0, потому что я инициализирую его в updateSettings (), и он не сбрасывается до тех пор, пока он не будет вызван снова (а затем myFucntion использует другие 2 параметра в памяти).

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

1 Ответ

2 голосов
/ 15 апреля 2011

Я бы не стал доверять этому коду. Я считаю, что ему не хватает надлежащих барьеров памяти для всей памяти, используемой разными потоками, за исключением двух подсчетов ссылок.

Для меня это выглядит как хорошее приложение для shared_mutex .

Edit:

20.7.2.2 [util.smartptr.shared] / p4 говорит:

В целях определения наличие данных гонки, член функции должны иметь доступ и изменять только объекты shared_ptr и weak_ptr сами по себе, а не объекты, которые они ссылаются к.

Однако вместо использования shared_mutex другим вариантом может быть использование API в 20.7.2.5 атомном доступе shared_ptr [util.smartptr.shared.atomic]:

Параллельный доступ к 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 будет легче понять правильно. Но API atomic shared_ptr может дать решение с более высокой производительностью.

Обновление:

Вот непроверенный код для решения 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
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...