2017-11-27 7 views
2

Ich möchte so etwas tun:Mutex in operator []

class BlockingVector{ 
protected: 
    std::vector<int> m_vector; 
    std::mutex m_mutex; 
public: 
    int & operator [](size_t p_index){ 
     std::lock_guard<std::mutex> lock(m_mutex); 
     return m_vector[p_index]; 
    } 
}; 

Ich weiß, das ist völlig falsch. Gibt es eine Möglichkeit, den Operator [] mit Mutex zu überladen?

Wie Nikos C sagt: Es gibt einen Verweis auf das Element, keine Kopie. Daher ist es nicht threadsicher, dieses Element zu verwenden.

+0

Was möchten Sie erreichen? – MFisherKDX

+0

Warum sollte es falsch sein? Was sind die Probleme mit dem Code? Wie benutzt man es? Bitte [lesen Sie, wie Sie gute Fragen stellen können] (http://stackoverflow.com/help/how-to-ask) und erfahren Sie, wie Sie ein [minimales, ** vollständiges ** und verifizierbares Beispiel] erstellen können (http: //stackoverflow.com/help/mcve). –

+0

Was lässt dich denken, dass das "total falsch" ist? – Yuushi

Antwort

1

Sie könnten eine ‚locked_reference‘ Helfer-Klasse definieren, die die Sperre in seinem Konstruktor und gibt in dem destructor und verwenden, die als Rückgabewert der Betreiber erwirbt []:

template<class T> class BlockingVector{ 
private: 
    std::vector<T> m_vector; 
    std::recursive_mutex m_mutex; 
    class locked_ref { 
     T &ref 
     lock_guard<std::recursive_mutex> lock; 
    public: 
     locked_ref(T &r, std::recursive_mutex &m) : ref(r), lock(m) {} 
     locked_ref(locked_ref &&) = default; 
     const T &operator=(const T &v) const { return ref = v; } 
     operator T() const { return ref; } 
    }; 
public: 
    locked_ref operator [](size_t p_index){ 
     return locked_ref(m_vector[p_index], m_mutex); } 
}; 

Sie benötigen einen recursive_mutex als ein Thread müssen

v[i] = v[j] + v[k]; 

und es gibt eine Gefahr, Deadlock, wenn zwei Threads gleichzeitig auf zwei Vektoren arbeiten: es mehrfach zu sperren so etwas wie evalute.

1

Nein. Der Aufrufer muss eine Sperre erfassen, da er anstelle einer Kopie einen Verweis auf die Daten erhält. Sie können nicht irgendwie auf einen int Thread-Safe für alle Code verweisen, der es verwendet.

Wenn Sie eine threadsichere Operation für die Daten benötigen und nicht möchten, dass der Aufrufer verantwortlich ist, haben Sie zwei Möglichkeiten: a) Fügen Sie diese Operation in der API Ihrer Klasse hinzu oder b) implementieren Sie einen Wrappertyp um die int Referenz.

Ein Wrapper kann kompliziert zu implementieren sein, da Sie sich nun um jeden möglichen Fall kümmern müssen. Ist es kopierbar? Beweglich? Beide auf thread-sichere Weise?

Also würde ich opt für das Hinzufügen der Operation als API. Wie:

class BlockingVector{ 
    // ... 

    void setVal(size_t index, int newVal) 
    { 
     std::lock_guard<std::mutex> lock(m_mutex); 
     m_vector[index] = newVal; 
    } 

    // Change: return copy, not reference. 
    int operator [](size_t p_index) const { 
     std::lock_guard<std::mutex> lock(m_mutex); 
     return m_vector[p_index]; 
    } 
}; 

Dies kann jedoch eine verwirrende API sein. So würde ich vorschlagen, nicht eine normale Funktion Überlastung operator[] statt:

int getVal(size_t p_index) const { 
    std::lock_guard<std::mutex> lock(m_mutex); 
    return m_vector[p_index]; 
} 

Beachten Sie, dass der Lage sein, den Mutex in einer const Funktion zu sperren, müssen Sie es wandelbar machen:

class BlockingVector{ 
    // ... 
    mutable std::mutex m_mutex; 

Sie lesen sollte mutexes für weitere Informationen über und const Funktionen:

Should mutexes be mutable?

+0

Ein kleines Wunder. Funktion kann const sein, wenn Mutex verwendet wird (std :: lock_guard lock (m_mutex);)? –

+1

@ HoànTrần Es ist üblich, Mutexe als "veränderbar" zu kennzeichnen. In diesem Fall müssten Sie den Mutex mit "mutable std :: mutex m_mutex;" deklarieren. Dann können Sie es sogar in 'const' Funktionen sperren. Ich empfehle Ihnen jedoch, dies zu lesen: https://stackoverflow.com/questions/4127333/should-mutexes-be-mutable#4128689 –