Betrachten Sie den folgenden C++ 11-Code, wobei die Klasse B
instanziiert und von mehreren Threads verwendet wird. Weil B
einen geteilten Vektor ändert, muss ich den Zugriff darauf in der ctor- und Mitgliedsfunktion foo von B
sperren. Um die Elementvariable id
zu initialisieren, verwende ich einen Zähler, der eine atomare Variable ist, weil ich von mehreren Threads darauf zugreife.Thread-sichere Initialisierung der atomaren Variablen in C++
struct A {
A(size_t id, std::string const& sig) : id{id}, signature{sig} {}
private:
size_t id;
std::string signature;
};
namespace N {
std::atomic<size_t> counter{0};
typedef std::vector<A> As;
std::vector<As> sharedResource;
std::mutex barrier;
struct B {
B() : id(++counter) {
std::lock_guard<std::mutex> lock(barrier);
sharedResource.push_back(As{});
sharedResource[id].push_back(A("B()", id));
}
void foo() {
std::lock_guard<std::mutex> lock(barrier);
sharedResource[id].push_back(A("foo()", id));
}
private:
const size_t id;
};
}
Leider enthält dieser Code eine Race-Bedingung und nicht funktioniert wie folgt aus (manchmal die Ctor und foo() nicht die gleiche ID verwenden). Wenn ich die Initialisierung von id an den Ctor Körper zu bewegen, die durch einen Mutex gesperrt ist, funktioniert es:
struct B {
B() {
std::lock_guard<std::mutex> lock(barrier);
id = ++counter; // counter does not have to be an atomic variable and id cannot be const anymore
sharedResource.push_back(As{});
sharedResource[id].push_back(A("B()", id));
}
};
Können Sie mir bitte helfen, zu verstehen, warum das letztere Beispiel funktioniert (ist es, weil es nicht die gleiche Mutex nicht verwendet ?)? Gibt es eine sichere Möglichkeit, id
in der Initialisierungsliste von B
zu initialisieren, ohne es in den Körper des Ctor zu sperren? Meine Anforderungen sind, dass id
const
sein muss und dass die Initialisierung von id
in der Initialisierungsliste stattfindet.
Können Sie den tatsächlichen Code, der das Problem verursacht, posten. Der Code, den Sie gestellt haben, macht keinen Sinn (zumindest in Ermangelung einer Definition von "A"). Zum Beispiel können Sie nicht einfach auf 'sharedResource [id]' zugreifen, ohne tatsächlich etwas getan zu haben, um 'sharedResource' so zu verändern, dass sie' id + 1' Elemente enthält. Und wenn 'A' keine Memberfunktion' push_back' enthält, sollte der Code nicht einmal kompiliert werden. –
@JamesKanze warum sollte 'A' ein' push_back' Mitglied brauchen? Ich sehe nur einen '(const char *, size_t)' -Konstruktor und einen Move/Copy-Konstruktor in Benutzung. OP: Bitte machen Sie dies ein [SSCCE] (http://sscce.org) wenn möglich – je4d
@ je4d: 'sharedResource' ist ein' std :: vector ', so' sharedResource [id] 'gibt ein' A & 'zurück und 'sharedResource [id] .push_back (...)' ruft somit 'A :: push_back' auf. – ildjarn