Die C ++ - FAQ wird in der Regel von der C ++ - Community verwaltet. Sie können uns in unserem Chat um Meinungen bitten.
Welpe
@DeadMG: Ich war mir der C ++ - FAQ und ihrer Etikette nicht bewusst, es wurde in einem Kommentar vorgeschlagen.
K-Ballo
2
Wo haben Sie gehört, dass const threadsicher bedeutet?
Mark B
2
@ Mark B: Herb Sutter und Bjarne Stroustrup sagten dies bei der Standard C ++ Foundation , siehe den Link am Ende der Antwort.
K-Ballo
HINWEIS FÜR DIE, DIE HIER KOMMEN: Die eigentliche Frage ist NICHT, ob constdies threadsicher bedeutet . Das wäre Unsinn, da es sonst bedeuten würde, dass Sie in der Lage sein sollten, jede thread-sichere Methode als zu markieren const. Die Frage, die wir wirklich stellen, ist constIMPLIES threadsicher, und darum geht es in dieser Diskussion.
user541686
Antworten:
131
Ich höre, dass constdies in C ++ 11 threadsicher bedeutet . Ist das wahr?
Es ist etwas wahr ...
Dies ist, was die Standardsprache zur Thread-Sicherheit zu sagen hat:
[1.10 / 4]
Zwei Ausdrucksbewertungen stehen in Konflikt, wenn einer von ihnen einen Speicherort (1.7) ändert und der andere auf denselben Speicherort zugreift oder diesen ändert.
[1.10 / 21]
Die Ausführung eines Programms enthält ein Datenrennen, wenn es zwei widersprüchliche Aktionen in verschiedenen Threads enthält, von denen mindestens eine nicht atomar ist und keine vor der anderen stattfindet. Ein solches Datenrennen führt zu undefiniertem Verhalten.
das ist nichts anderes als die hinreichende Bedingung für ein Daten Rennen auftreten:
Es werden zwei oder mehr Aktionen gleichzeitig für eine bestimmte Sache ausgeführt. und
Mindestens einer von ihnen ist ein Schreiben.
Die Standardbibliothek baut darauf auf und geht noch ein bisschen weiter:
[17.6.5.9/1] In
diesem Abschnitt werden die Anforderungen festgelegt, die Implementierungen erfüllen müssen, um Datenrennen zu verhindern (1.10). Jede Standardbibliotheksfunktion muss jede Anforderung erfüllen, sofern nicht anders angegeben. Implementierungen können Datenrennen in anderen als den unten angegebenen Fällen verhindern.
[17.6.5.9/3]
Eine C ++ - Standardbibliotheksfunktion darf Objekte (1.10), auf die andere Threads als der aktuelle Thread zugreifen , nicht direkt oder indirekt ändern, es sei denn, auf die Objekte wird direkt oder indirekt über die nicht konstanten Argumenteder Funktion zugegriffen, einschließlichthis.
was in einfachen Worten besagt, dass erwartet wird, dass Operationen an constObjekten threadsicher sind . Dies bedeutet, dass die Standardbibliothek kein Datenrennen einführt, solange Operationen an constObjekten Ihres eigenen Typs ausgeführt werden
Besteht ausschließlich aus Lesungen - das heißt, es gibt keine Schreibvorgänge -; oder
Synchronisiert intern Schreibvorgänge.
Wenn diese Erwartung für einen Ihrer Typen nicht gilt, kann die direkte oder indirekte Verwendung zusammen mit einer Komponente der Standardbibliothek zu einem Datenrennen führen . Zusammenfassend constbedeutet dies aus Sicht der Standardbibliothek threadsicher . Es ist wichtig zu beachten, dass dies lediglich ein Vertrag ist und vom Compiler nicht durchgesetzt wird. Wenn Sie ihn brechen, erhalten Sie undefiniertes Verhalten und sind auf sich allein gestellt. Ob vorhanden ist oder nicht , wird nicht die Codegenerierung --at dest nicht in Bezug auf die Auswirkungen auf Daten Rennen -.const
Heißt das constjetzt das Äquivalent von Java ‚s synchronized?
Nein . Überhaupt nicht...
Betrachten Sie die folgende stark vereinfachte Klasse, die ein Rechteck darstellt:
Die Member-Funktionarea ist threadsicher ; nicht weil es ist const, sondern weil es ausschließlich aus Leseoperationen besteht. Es sind keine Schreibvorgänge beteiligt, und mindestens ein Schreibvorgang ist erforderlich, damit ein Datenrennen stattfinden kann. Das bedeutet, dass Sie areavon so vielen Threads aus aufrufen können, wie Sie möchten, und dass Sie jederzeit korrekte Ergebnisse erhalten.
Beachten Sie, dass dies nicht bedeutet , dass rectist Thread-sicher . In der Tat, um zu sehen , ist es einfach , wie wenn ein Aufruf areazur gleichen Zeit geschehen sollte , dass ein Aufruf set_sizezu einem bestimmten rect, dann areakönnte am Ende das Ergebnis basiert auf einer alten Breite und eine neue Höhe (oder sogar auf verstümmelte Werte) Berechnen .
Aber das ist in Ordnung, es rectist nicht constso, dass nicht einmal erwartet wird, dass es threadsicher ist . Ein deklariertes Objekt const rectwäre dagegen threadsicher, da keine Schreibvorgänge möglich sind (und wenn Sie überlegen, const_castetwas ursprünglich Deklariertes zu consttun, erhalten Sie ein undefiniertes Verhalten, und das war's).
Was bedeutet es dann?
Nehmen wir aus Gründen der Argumentation an, dass Multiplikationsoperationen extrem kostspielig sind und wir sie besser vermeiden, wenn dies möglich ist. Wir könnten den Bereich nur berechnen, wenn er angefordert wird, und ihn dann zwischenspeichern, falls er in Zukunft erneut angefordert wird:
[Wenn dieses Beispiel zu künstlich erscheint, können Sie es mental intdurch eine sehr große dynamisch zugewiesene Ganzzahl ersetzen, die von Natur aus nicht threadsicher ist und für die Multiplikationen äußerst kostspielig sind.]
Die Member-Funktionarea ist nicht mehr threadsicher , sie schreibt jetzt und ist nicht intern synchronisiert. Ist es ein Problem? Der Aufruf areaals Teil eines passieren kann Kopie-Konstruktor eines anderen Objekts, wie Konstruktor könnte durch eine Operation an einem genannt wurden Standard - Container , und an diesem Punkt die Standardbibliothek diese Operation erwartet als verhalten Lese in Bezug auf Daten Rennen . Aber wir schreiben!
Sobald wir einen direkt oder indirekt rectin einen Standardcontainer legen, schließen wir einen Vertrag mit der Standardbibliothek . Um weiterhin Schreibvorgänge in einer constFunktion ausführen zu können, während dieser Vertrag weiterhin eingehalten wird, müssen diese Schreibvorgänge intern synchronisiert werden:
Beachten Sie, dass wir die areaFunktion threadsicher gemacht haben , die aber rectimmer noch nicht threadsicher ist . Ein Aufruf zur areagleichen Zeit, zu der ein Aufruf an set_sizeimmer noch den falschen Wert berechnet, da die Zuweisungen zu widthund heightnicht durch den Mutex geschützt sind.
Wenn wir wirklich einen Thread-Safe wollten rect, würden wir ein Synchronisationsprimitiv verwenden, um den Nicht-Thread-Safe zu schützen rect.
Gehen ihnen die Schlüsselwörter aus ?
Ja, sind Sie. Seit dem ersten Tag gehen ihnen die Schlüsselwörter aus .
@ Ben Voigt: Nach meinem Verständnis ist die C ++ 11- Spezifikation für std::stringso formuliert, dass COW bereits verboten ist . Ich erinnere mich jedoch nicht an die Einzelheiten ...
K-Ballo
3
@ BenVoigt: Nein. Es würde lediglich verhindern, dass solche Dinge nicht synchronisiert werden, dh nicht threadsicher. C ++ 11 verbietet COW bereits explizit - diese spezielle Passage hat jedoch nichts damit zu tun und würde COW nicht verbieten.
Welpe
2
Es scheint mir, dass es eine logische Lücke gibt. [17.6.5.9/3] verbietet "zu viel", indem er sagt "es darf nicht direkt oder indirekt modifizieren"; es sollte heißen "soll nicht direkt oder indirekt ein Datenrennen einführen", es sei denn, ein atomares Schreiben ist irgendwo definiert, um nicht "modifizieren" zu sein. Aber ich kann das nirgendwo finden.
Andy Prowl
1
Wahrscheinlich habe ich hier meinen ganzen Punkt etwas klarer formuliert : isocpp.org/blog/2012/12/… Vielen Dank, dass Sie versucht haben, trotzdem zu helfen.
Andy Prowl
1
manchmal frage ich mich, wer derjenige war (oder diejenigen, die direkt beteiligt waren), der tatsächlich dafür verantwortlich war, einige Standardabsätze wie diese aufzuschreiben.
const
dies threadsicher bedeutet . Das wäre Unsinn, da es sonst bedeuten würde, dass Sie in der Lage sein sollten, jede thread-sichere Methode als zu markierenconst
. Die Frage, die wir wirklich stellen, istconst
IMPLIES threadsicher, und darum geht es in dieser Diskussion.Antworten:
Es ist etwas wahr ...
Dies ist, was die Standardsprache zur Thread-Sicherheit zu sagen hat:
das ist nichts anderes als die hinreichende Bedingung für ein Daten Rennen auftreten:
Die Standardbibliothek baut darauf auf und geht noch ein bisschen weiter:
was in einfachen Worten besagt, dass erwartet wird, dass Operationen an
const
Objekten threadsicher sind . Dies bedeutet, dass die Standardbibliothek kein Datenrennen einführt, solange Operationen anconst
Objekten Ihres eigenen Typs ausgeführt werdenWenn diese Erwartung für einen Ihrer Typen nicht gilt, kann die direkte oder indirekte Verwendung zusammen mit einer Komponente der Standardbibliothek zu einem Datenrennen führen . Zusammenfassend
const
bedeutet dies aus Sicht der Standardbibliothek threadsicher . Es ist wichtig zu beachten, dass dies lediglich ein Vertrag ist und vom Compiler nicht durchgesetzt wird. Wenn Sie ihn brechen, erhalten Sie undefiniertes Verhalten und sind auf sich allein gestellt. Ob vorhanden ist oder nicht , wird nicht die Codegenerierung --at dest nicht in Bezug auf die Auswirkungen auf Daten Rennen -.const
Nein . Überhaupt nicht...
Betrachten Sie die folgende stark vereinfachte Klasse, die ein Rechteck darstellt:
Die Member-Funktion
area
ist threadsicher ; nicht weil es istconst
, sondern weil es ausschließlich aus Leseoperationen besteht. Es sind keine Schreibvorgänge beteiligt, und mindestens ein Schreibvorgang ist erforderlich, damit ein Datenrennen stattfinden kann. Das bedeutet, dass Siearea
von so vielen Threads aus aufrufen können, wie Sie möchten, und dass Sie jederzeit korrekte Ergebnisse erhalten.Beachten Sie, dass dies nicht bedeutet , dass
rect
ist Thread-sicher . In der Tat, um zu sehen , ist es einfach , wie wenn ein Aufrufarea
zur gleichen Zeit geschehen sollte , dass ein Aufrufset_size
zu einem bestimmtenrect
, dannarea
könnte am Ende das Ergebnis basiert auf einer alten Breite und eine neue Höhe (oder sogar auf verstümmelte Werte) Berechnen .Aber das ist in Ordnung, es
rect
ist nichtconst
so, dass nicht einmal erwartet wird, dass es threadsicher ist . Ein deklariertes Objektconst rect
wäre dagegen threadsicher, da keine Schreibvorgänge möglich sind (und wenn Sie überlegen,const_cast
etwas ursprünglich Deklariertes zuconst
tun, erhalten Sie ein undefiniertes Verhalten, und das war's).Nehmen wir aus Gründen der Argumentation an, dass Multiplikationsoperationen extrem kostspielig sind und wir sie besser vermeiden, wenn dies möglich ist. Wir könnten den Bereich nur berechnen, wenn er angefordert wird, und ihn dann zwischenspeichern, falls er in Zukunft erneut angefordert wird:
[Wenn dieses Beispiel zu künstlich erscheint, können Sie es mental
int
durch eine sehr große dynamisch zugewiesene Ganzzahl ersetzen, die von Natur aus nicht threadsicher ist und für die Multiplikationen äußerst kostspielig sind.]Die Member-Funktion
area
ist nicht mehr threadsicher , sie schreibt jetzt und ist nicht intern synchronisiert. Ist es ein Problem? Der Aufrufarea
als Teil eines passieren kann Kopie-Konstruktor eines anderen Objekts, wie Konstruktor könnte durch eine Operation an einem genannt wurden Standard - Container , und an diesem Punkt die Standardbibliothek diese Operation erwartet als verhalten Lese in Bezug auf Daten Rennen . Aber wir schreiben!Sobald wir einen direkt oder indirekt
rect
in einen Standardcontainer legen, schließen wir einen Vertrag mit der Standardbibliothek . Um weiterhin Schreibvorgänge in einerconst
Funktion ausführen zu können, während dieser Vertrag weiterhin eingehalten wird, müssen diese Schreibvorgänge intern synchronisiert werden:Beachten Sie, dass wir die
area
Funktion threadsicher gemacht haben , die aberrect
immer noch nicht threadsicher ist . Ein Aufruf zurarea
gleichen Zeit, zu der ein Aufruf anset_size
immer noch den falschen Wert berechnet, da die Zuweisungen zuwidth
undheight
nicht durch den Mutex geschützt sind.Wenn wir wirklich einen Thread-Safe wollten
rect
, würden wir ein Synchronisationsprimitiv verwenden, um den Nicht-Thread-Safe zu schützenrect
.Ja, sind Sie. Seit dem ersten Tag gehen ihnen die Schlüsselwörter aus .
Quelle : Sie wissen es nicht
const
undmutable
- Herb Sutterquelle
std::string
so formuliert, dass COW bereits verboten ist . Ich erinnere mich jedoch nicht an die Einzelheiten ...