Say zwei Funktionen gibt es den Durchschnitt einer Eigenschaft zu aktualisieren und zurück gemessen wird:Veröffentlichung Semantics erwerben Berechne Durchschnitt
void Class::Update(int delta)
{
m_accumulatedValue += delta;
++ m_count;
}
double Class::GetAverage()
{
return m_accumulatedValue/(double)m_count;
}
Nun nehmen wir sie in einer Multithread-Umgebung mit einem Gewinde geändert werden müssen laufen Pool, in dem ein Thread angefordert wird einen von ihnen auszuführen - das heißt, der Faden jedem von ihnen die Ausführung ein anderer jedes Mal sein:
std::atomic<int> m_accumulatedValue;
std::atomic<int> m_count;
// ...
void Class::Update(int delta)
{
m_accumulatedValue.fetch_add(delta , std::memory_order_relaxed);
m_count.fetch_add(1 , std::memory_order_release);
}
double Class::GetAverage()
{
auto count = m_count.load(std::memory_order_acquire);
auto acc = m_accumulatedValue.load(std::memory_order_relaxed);
return acc/(double)count;
}
ich versuche, die acquire zu verstehen und Speicher freigeben Bestellungen.
Angenommen, es gibt keine gleichzeitigen Aufrufe desselben Objekts für Update()
, aber möglicherweise gleichzeitige Aufrufe desselben Objekts für Update()
und GetAverage()
.
Für das, was ich gelesen habe, verbietet die acquire Last von m_count
in GetAverage()
die Umordnung der Last von m_accumulatedValue
, bevor er und zugleich garantiert, dass jede Änderung m_accumulatedValue
ausgeführt durch Update()
durch den Thread calling sichtbar ist GetAverage()
Sobald der Wechsel zu m_count
ist auch zu sehen, für den Speicher durchgeführt m_cout
von Update()
hat eine Freigabe bestellen.
Ist das, was ich gerade gesagt habe, richtig?
Gibt GetAverage()
(mit der besagten Garantie der Nicht-Gleichzeitigkeit der Anrufe zu Update()
) immer die richtige Antwort zurück? Oder kann es einen Weg geben, den berechneten Durchschnitt mit einigen der Werte "aktueller" als den anderen zurück zu geben?
Muss m_accumulatedValue
überhaupt atomar sein?
'memory_order_relaxed' verbietet dem anderen Thread nicht, den neuen Wert zu sehen, es erfordert es einfach nicht.Es könnte also immer noch eine Wettlaufsituation geben. –
Abgesehen von der Semantik der Speicherordnung ist dieser Code gebrochen. Eine mögliche Ausführungsreihenfolge besteht darin, dass ein Thread zu 'm_accumulatedValue' addiert, dann ein anderer Thread' m_accumulatedValue' ** und ** 'm_count' liest, dann aktualisiert der erste Thread' m_count'. Der vom zweiten Thread berechnete Durchschnittswert ist falsch. –
Nein. Vielleicht. Warum nicht gegenseitiger Ausschluss? – jotik