2015-06-16 9 views
5

Ich spiele mit std::atomic herum, aber ich denke, dass ich das Konzept nicht völlig verstanden habe. Ich frage mich, warum es keine atomaren Container gibt. Also habe ich ein bisschen herumgespielt. Zuerst habe ich versucht, die folgenden:C++ atomischer Listencontainer

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

Aber wie schon einige andere Leute darauf hingewiesen, dass dies nicht funktioniert, weil der Konstruktor noexcept ist. Also habe ich eine Art von 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; 

Jetzt habe ich mit ihm gearbeitet, aber ich entdeckte, dass es nicht richtig funktioniert und ich Segmentation fault Fehler erhalten.

Kann jemand bitte erklären, warum es so nicht möglich ist, eine atomare Liste zu erstellen?

EDIT: Wenn jemand will, wie wirklich mein Test sehen Programm zum besseren Verständnis aussieht:

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

Antwort

4

Das erste und wichtigste Problem: das kann unmöglich funktionieren. Sie benötigen eine Synchronisation um die Ausführung der Member-Funktionen, nicht um das Abrufen der Liste. std::atomic beginnt nicht einmal zu ähneln, was Sie brauchen.

In Bezug auf Ihre versuchte Implementierung kann ein atomic<T> zu T& Casting nichts vernünftiges tun.

Und selbst wenn es sinnvoll wäre, würde eine solche Besetzung die Atomarität Ihres Objekts völlig vergessen, und alles, was Sie mit den Referenzen tun, wird also keine atomaren Operationen sein.

+0

OK ich verstehe. Ist es also tatsächlich möglich, einen STL-Container * atomar zu konvertieren? Oder ist es notwendig, es von Grund auf neu zu erstellen? –

+1

@Thomas: Du könntest versuchen, einen Container-Adapter zu schreiben, aber ich denke, du wärst viel besser dran, wenn du "von Null an schreibst, aber vielleicht intern einen Standard-Container verwendest". Zumal das 'Container'-Konzept tatsächlich * unmöglich ist * atomar zu machen, es sei denn, ihr' value_type' ist ein spezielles Objekt, das mit dem Container kooperiert, um sich atomar zu verhalten (zB wie könnte 'C [i] = x;' eine atomare Operation?). – Hurkyl

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

std::atomic keinen Konvertierungsoperator auf eine Referenz liefert. Hätten Sie einen static_cast verwendet, würde es nicht einmal kompilieren, aber hier interpretiert der C-Cast den std::atomic<MyList<int>> direkt als MyList<int>, was gute Gründe hat, nicht zu funktionieren.

Sie können nicht direkt ändern, was in einem std::atomic ist. Sie müssen eine Kopie der Daten mit load() abrufen, diese Kopie ändern und dann mit store() zurück austauschen.

+0

So sollte es funktionieren Ich habe: 'MyList temp = l.load()' dann ändern Sie 'temp' und speichern Sie es zurück mit' l.store (temp) 'Können Sie das bitte zu Ihrer Antwort hinzufügen? –

+0

Und noch eine Sache: Also was ist der 'operator()' für wenn ich ihn nicht direkt benutzen kann? –

+0

@ThomasSparber ja, würde es. Aber wie Hurkyl darauf hinweist, ist das einzige, was "std :: atomic" bietet, eine atomare Lade-/Speicheroperation. Es synchronisiert nicht, was Sie tatsächlich mit den Daten tun, es stellt nur sicher, dass wenn Sie es für neue Daten auslagern, jeder ein komplettes Objekt erhält. Der Konvertierungsoperator konvertiert zu "T", nicht zu "T &" und ist nur syntaktischer Zucker über einem 'load()'. – Quentin