2015-06-16 13 views
5

Sto giocando con std::atomic ma penso di non aver capito completamente il concetto. Mi chiedo perché non ci sono contenitori atomici. Così ho giocato un po 'in giro. Per prima cosa ho provato quanto segue:Contenitore di lista atomica C++

std::atomic<std::list<int> > atomicList; 

Ma, come già altre persone sottolineato che questo non funziona perché il costruttore è noexcept. Così ho creato una sorta di hack:

template<class T> 
class MyList 
{ 
public: 
    //Make sure that no exception is thrown 
    MyList() noexcept 
     try: l() 
    {}catch(...) {} 

    void push_back(const T &t) { l.push_back(t); } 

    void pop_front() { l.pop_front(); } 

    size_t size() const { return l.size(); } 

private: 
    list<T> l; 

}; 

atomic<MyList<int> > atomicList; 

Ora ho lavorato con lui, ma ho scoperto che non funziona correttamente e si verificano errori di segmentation fault.

Qualcuno può spiegare perché non è possibile creare una lista atomica in questo modo?

EDIT: Se qualcuno vuole vedere come il mio programma di test sembra davvero per una migliore comprensione:

#include <list> 
#include <thread> 
#include <sys/time.h> 
#include <iostream> 
#include <atomic> 

using namespace std; 

template<class T> 
class MyList 
{ 

public: 
    MyList() noexcept 
     try: l() 
    {}catch(...) {} 

    void push_back(const T &t) { l.push_back(t); } 

    void pop_front() { l.pop_front(); } 

    size_t size() const { return l.size(); } 

private: 
    list<T> l; 

}; 

atomic<MyList<int> > l; 

void work() 
{ 
    for(unsigned int i = 0; i < 100000; ++i) 
    { 
     //Called operator() 
     ((MyList<int>&)l).push_back(i); 
     ((MyList<int>&)l).push_back(((MyList<int>&)l).size()); 
     ((MyList<int>&)l).pop_front(); 
    } 
} 

int main(int argc, char *args[]) 
{ 
    struct timeval time1; 
    struct timeval time2; 
    gettimeofday(&time1, 0); 
    thread t1(work); 
    thread t2(work); 
    thread t3(work); 
    thread t4(work); 
    t1.join(); 
    t2.join(); 
    t3.join(); 
    t4.join(); 
    gettimeofday(&time2, 0); 
    cout<<((time2.tv_sec-time1.tv_sec)+double(time2.tv_usec-time1.tv_usec)/1000000)<<endl; 
} 

risposta

4

Il primo e più importante problema: questo non può funzionare. È necessaria la sincronizzazione sull'esecuzione delle funzioni membro, non attorno al recupero dell'elenco. std::atomic non inizia nemmeno ad assomigliare a quello che ti serve.

Per quanto riguarda il tentativo di implementazione, la trasmissione di un numero da atomic<T> a T& non può fare nulla di ragionevole.

E anche se fosse significativo, un tale cast dimenticherebbe completamente l'atomicità del tuo oggetto, e quindi qualsiasi cosa tu faccia con i riferimenti non saranno operazioni atomiche.

+0

OK Capisco. Quindi è effettivamente possibile * convertire * un container stl in atomico? O è necessario crearlo da zero? –

+1

@Thomas: potresti provare a scrivere un adattatore per contenitore, ma penso che sarebbe molto meglio pensare in termini di "scrittura da zero, ma forse utilizzare internamente un contenitore standard". Soprattutto perché il concetto di 'Container' è in realtà * impossibile * da rendere atomico, a meno che il loro' value_type' sia un oggetto specializzato che coopera con il contenitore per comportarsi atomicamente (es. Come potrebbe 'C [i] = x;' può essere implementato come un'operazione atomica?). – Hurkyl

1
((MyList<int>&)l).push_back(i); 

std::atomic non fornisce un operatore di conversione a un riferimento. Se avessi usato uno static_cast, non lo compilerebbe nemmeno, ma qui il C-cast reinterpreta direttamente lo std::atomic<MyList<int>> come MyList<int>, che ha buoni motivi per non funzionare.

Non è possibile modificare direttamente ciò che si trova in un std::atomic. È necessario recuperare una copia dei dati con load(), modificare questa copia e quindi sostituirla con store().

+0

Quindi dovrei farlo: 'MyList temp = l.load()' quindi modifica 'temp' e lo memorizza usando' l.store (temp) 'Puoi aggiungerlo alla tua risposta? –

+0

E un'altra cosa: che cosa è il 'operator()' se non posso usarlo direttamente? –

+0

@ThomasSparber si, lo farebbe. Ma come sottolinea Hurkyl, l'unica cosa che 'std :: atomic' fornisce è un'operazione di carico/immagazzinamento atomico. Non sincronizza ciò che effettivamente fai ai dati, ma garantisce che quando si scambiano i nuovi dati tutti ottengono un oggetto completo. L'operatore di conversione converte in 'T', non' T &', ed è solo zucchero sintattico su 'load()'. – Quentin