Wat kan er gebeuren als twee threads tegelijkertijd toegang hebben tot dezelfde boolvariabele?

Ik heb een cross-platform c ++ -programma waarbij ik de boost-bibliotheken gebruik om een ​​asynchrone timer te maken.
Ik heb een globale variabele:

bool receivedInput = false;

Eén thread wacht op en verwerkt de invoer

string argStr;
while (1) 
{
     getline(cin, argStr);
     processArguments(argStr);
     receivedInput = true;
}

De andere thread heeft een timer waarop een callback elke 10 seconden wordt aangeroepen. In die callback controleer ik of ik een bericht heb ontvangen

if (receivedInput)
{
    //set up timer to fire again in 10 seconds
    receivedInput = false;
}
else
    exit(1);

Dus is dit veilig? Voor de inlezing in thread 2, denk ik dat het niet uitmaakt, omdat de voorwaarde zal evalueren naar waar of onwaar. Maar ik ben niet zeker wat er zou gebeuren als beide threads tegelijkertijd receiveInput proberen in te stellen. Ik heb mijn timer ook 3x langer gemaakt dan de periode dat ik verwacht input te ontvangen, dus ik maak me geen zorgen over een race-situatie.

Edit: To solve this I used boost::unique_lock when I set receivedInput and boost::shared_lock when I read receivedInput. I used an example from here

4
¤ als je je afvraagt ​​of het veilig is, dan is het dat niet. het maakt niet uit of iets technisch veilig is of niet, met threading is de situatie zodanig dat als je het niet begrijpt, het in korte tijd fouten zal ontwikkelen, zelfs als het perfect is om mee te beginnen. de code is namelijk technisch onveilig maar praktisch veilig. technisch kan bijvoorbeeld de "controlerende" thread worden opgeschort op de if terwijl de "input" thread zeven regels verwerkt. in de praktijk zal dat echter niet gebeuren, maar met de code zoals die Bad Things zal gebeuren. :-) Cheers & hth.
toegevoegd de auteur Cheers and hth. - Alf, de bron

3 antwoord

Dit is fundamenteel onveilig. Nadat thread 1 true heeft geschreven naar receivedInput , kan thread 2 de nieuwe waarde niet zien. De compiler kan bijvoorbeeld uw code optimaliseren door bepaalde aannames te doen over de waarde van receivedInput op het moment dat deze wordt gebruikt als de if-voorwaarde of deze in een register te cachen, zodat u niet zeker bent dat het hoofdgeheugen daadwerkelijk gelezen op het moment dat de if-voorwaarde wordt geëvalueerd. Ook kunnen zowel de compiler als de CPU de volgorde van lezen en schrijven voor optimalisatie wijzigen, bijvoorbeeld true kan worden geschreven naar receiveInput vóór getLine() en processArguments() .

Bovendien is vertrouwen op timing voor synchronisatie een heel slecht idee, omdat je vaak geen garanties hebt met betrekking tot de hoeveelheid CPU-tijd die elke thread in een bepaald tijdsinterval krijgt of helemaal in een bepaald tijdsinterval.

Een veelgemaakte fout is om te denken dat het maken van receiveInput volatile hier kan helpen. In feite garandeert volatile dat waarden daadwerkelijk in het hoofdgeheugen worden gelezen/geschreven (in plaats van bijvoorbeeld in een register in de cache te worden geplaatst) en dat lees- en schrijfbewerkingen van de variabele ten opzichte van elkaar worden gerangschikt. Het biedt echter geen garantie dat de lees- en schrijfbewerkingen van de variabele volatile worden gerangschikt met betrekking tot andere instructies.

U hebt geheugenbarrières nodig of een goed synchronisatiemechanisme om dit te laten werken zoals u verwacht.

5
toegevoegd

Je zou je rijnorm moeten controleren. Ervan uitgaande dat we het hebben over POSIX-threads, is dit expliciet ongedefinieerd gedrag - een object is mogelijk niet toegankelijk voor één thread, terwijl een andere thread dit wel of niet aan het wijzigen is. Alles kan gebeuren.

1
toegevoegd
@SteveJessop Ik vergeet dat steeds!
toegevoegd de auteur David Schwartz, de bron
Met ingang van september 2011 is de standaard voor het inrijgen mogelijk de C ++ -norm!
toegevoegd de auteur Steve Jessop, de bron

Als uw threads de waarde van receivedInput gebruiken om onafhankelijke codeblokken aan te sturen, maar niet om met elkaar te synchroniseren, is er één eenvoudige oplossing:

voeg "volatile" toe vóór receivedInput, dus de compiler zal de optimalisatie niet uitvoeren en voorkomen dat de threads de waarde van receivedInput delen.

0
toegevoegd
Ik wil je niet -1 omdat je nieuw bent en ik wil niet dat je weggaat, maar ik wil niet dat de misvatting dat volatile nuttig is om multithreading te verspreiden. Het is niet nuttig, volatile heeft niets te maken met draadsnijden en ook niet als een methode van synchronisatie.
toegevoegd de auteur GManNickG, de bron
Merk echter op dat multi-threading veel meer inhoudt dan het uitschakelen van optimalisaties op een variabele.
toegevoegd de auteur GManNickG, de bron
Bedankt voor je reactie. Ja, ik heb hier een fout gemaakt, de twee threads wijzen een nieuwe waarde toe aan dezelfde variabele. Synchronisatie is hier nodig. Maar vluchtig is zeker gerelateerd aan multi-threading. "Over het algemeen is het vluchtige zoekwoord bedoeld om te voorkomen dat de compiler optimalisaties toepast op de code waarvan wordt aangenomen dat de waarden van variabelen niet" op zichzelf "kunnen veranderen. (Ik controleer het opnieuw op wikipedia) :)
toegevoegd de auteur xiesusu, de bron