2010-02-12 16 views
9

Ich habe eine Klasse, auf die aus mehreren Threads zugegriffen wird. Sowohl die Getter- als auch die Setter-Funktion sind mit Sperren geschützt. Werden die Sperren für Getterfunktionen benötigt? Warum?benötigt eine Getterfunktion einen Mutex?

class foo { 
public: 
    void setCount (int count) { 
     boost::lock_guard<boost::mutex> lg(mutex_); 
     count_ = count; 
    } 

    int count() { 
     boost::lock_guard<boost::mutex> lg(mutex_); // mutex needed? 
     return count_; 
    } 

private: 
    boost::mutex mutex_; 
    int count_; 
}; 

Antwort

13

Die einzige Möglichkeit, die Sperre zu umgehen, besteht darin, dass Sie sich davon überzeugen können, dass das System die überwachte Variable in allen Fällen atomar überträgt. Wenn Sie sich aus dem einen oder anderen Grund nicht sicher sein können, benötigen Sie den Mutex.

Für einen einfachen Typ wie ein int, können Sie sich selbst davon überzeugen, dass dies wahr ist, abhängig von der Architektur, und davon ausgegangen, dass es für die Übertragung einzelner Anweisungen ausgerichtet ist. Für jeden Typ, der komplizierter ist als das, musst du das Schloss haben.

4

Wenn Sie nicht über einen Mutex um den Getter haben, und ein Thread ist zu lesen, während ein anderer Thread sie schreibt, werden Sie lustige Ergebnisse.

+0

Nicht unbedingt. Das Lesen und Schreiben eines 'int' sind wahrscheinlich atomare Operationen. Natürlich ist dies architekturabhängig und nicht portabel. – Dan

+2

Genau - es ist undefiniertes Verhalten. Es ist besser, den Minutenaufwand einer Sperre zu haben, als wenn der Speicher beschädigt wäre. –

+0

Normalerweise, ja.Wenn Sie jedoch die Leistung nachweislich steigern möchten, ist es möglicherweise möglich, die Sperre zu überspringen. Siehe meine Antwort. – Dan

2

in Ihnen Fall wahrscheinlich nicht, wenn Ihre CPU ist 32 Bit, aber wenn Zählung ist ein komplexes Objekt oder CPU braucht mehr als einen Befehl um seinen Wert zu aktualisieren, dann ja

5

Ist der Mutex wirklich nur einen einzigen Schutz int? Es macht einen Unterschied - wenn es ein komplexerer Datentyp ist, müssen Sie definitiv sperren.

Aber wenn es nur ein int ist, und Sie sind sicher, dass int ein Atomtyp (dh der Prozessor nicht tun müssen, um zwei getrennte Speicher liest den int in ein Register zu laden), und Sie haben die gebenchmarkt Leistung und bestimmt, dass Sie eine bessere Leistung benötigen, dann können Sie in Erwägung ziehen, die Sperre sowohl vom Getter als auch vom Setter zu löschen. Wenn Sie das tun, stellen Sie sicher, dass Sie die int als volatile qualifizieren. Und schreibe einen Kommentar, der erklärt, warum du keinen Mutex-Schutz hast und unter welchen Bedingungen du ihn brauchst, wenn sich die Klasse ändert.

auch aufpassen, dass Sie nicht über Code wie folgt:

void func(foo &f) { 
    int temp = f.count(); 
    ++temp; 
    f.setCount(temp); 
} 

Das ist nicht thread, unabhängig davon, ob Sie einen Mutex verwenden oder nicht. Wenn Sie so etwas tun müssen, muss der Mutex-Schutz außerhalb der Setter/Getter-Funktionen liegen.

1

Die Sperre ist erforderlich, serialisieren Zugriff auf freigegebene Ressource. In Ihrem speziellen Fall könnten Sie nur mit atomic integer operations durchkommen, aber im Allgemeinen benötigen Sie für größere Objekte, die mehr als eine Bustransaktion benötigen, Sperren, um sicherzustellen, dass der Leser immer ein konsistentes Objekt sieht.

1

Es hängt von der genauen Implementierung des Objekts ab, das gesperrt wird. Im Allgemeinen möchten Sie jedoch nicht, dass jemand ein Objekt verändert (setzt?), Während jemand anderes gerade dabei ist, es zu lesen (zu bekommen?). Der einfachste Weg, dies zu verhindern, ist das Sperren eines Readers.

In komplizierteren Setups wird die Sperre so implementiert, dass beliebig viele Leute gleichzeitig lesen können, aber niemand kann schreiben, während jemand liest, und niemand kann lesen, während ein Schreibvorgang läuft.