2017-12-11 4 views
2

Ich möchte wissen, ist es möglich, Vergleich und Inkrement einer atomaren Variablen mit einer einzigen atomaren Operation durchzuführen. Dies ist, was ich geschrieben habe, so weit (Snippet-Code eines Threads)Wie man eine atomare Variable vergleicht und inkrementiert

std::atomic<int> counter; //global variable 

if(counter<25) 
{ 
counter++; 
} 
else 
{ 
    //send serial/socket data 
} 

Ich weiß, dass ich falsch mache, da Atom Variable Zähler zweimal zugegriffen wird (einmal für die Daten und andere für Zuwachs bekommen). Dies kann jedoch ein Problem verursachen, wenn ein anderer Thread nach dem Abrufen des Variablenwerts und vor dem Inkrementieren eine Aktualisierungsoperation für den Zähler ausführt. Also möchte ich wissen, ob es möglich ist, beide Operationen in einem einzigen Schuss auszuführen. Außerdem möchte ich Mutex nicht verwenden.

+0

Ihr Codebeispiel erscheint zwei verschiedene Top-Level-Variablen zu zeigen, dass beide Namen sind 'NoOfTimesSent' . Einer von ihnen wird als "atomar " deklariert, und der andere wird als "int" deklariert. Aber es sieht so aus, als hätten Sie einen Code weggelassen. Was geht wo die "..." sind? –

+0

@james Ich habe die Frage nach Ihrer Anfrage aktualisiert. Anfangs werde ich den Wert von counter im Hauptthread auf 1 setzen. – Harry

+1

@Harry Wenn Sie Ihr Snippet betrachten, scheint es, dass Sie nicht zulassen wollen, dass mehrere Threads auf einen der 'if else'-Blöcke zugreifen. In diesem Fall bin ich mir ziemlich sicher, dass du einen Mutex brauchst. – freakish

Antwort

1

In if(counter<25) counter++; gibt es eine Wettlaufsituation zwischen dem Lesen des Zählers und seiner Aktualisierung (d. H. Einer atomaren Ladung gefolgt von einer atomaren Ladung - Modify-Store).

Es braucht eine Vergleichsaustauschschleife, um sicherzustellen, dass der gelesene Wert sich seitdem nicht geändert hat. Wenn es sich geändert hat, muss die Operation wiederholt werden.

Etwas wie folgt aus:

std::atomic<int> counter; 

auto value = counter.load(std::memory_order_relaxed); 
while(value < 25) { 
    if(counter.compare_exchange_weak(value, value + 1, std::memory_order_release, std::memory_order_relaxed)) 
     break; // Succeeded incrementing counter. 
    // compare_exchange_weak failed because counter has changed. 
    // compare_exchange_weak reloaded value with the new value of counter. 
    // Retry. 
} 
if(!(value < 25)) 
    // Failed to increment because counter is not less than 25. 
+0

Beachten Sie, dass dies möglicherweise nicht der exakt entsprechende Code ist. Denken Sie an CompareExchange, um zu überprüfen, ob der Wert, den Sie erhalten haben, noch gültig ist, bevor Sie den neuen Wert schreiben. –

+0

@GemTaylor Ihre Aussage ist falsch. Sie vermissen wahrscheinlich die Tatsache, dass 'compare_exchange_weak' den neuen Wert in sein erstes Argument lädt, wenn er sich geändert hat. –

+0

Ich bin mir dessen bewusst. Es war die While-Schleife, die ich nicht mochte. Ja, Sie könnten damit durchkommen. –

-1

näher Code könnte "so etwas wie":

const int GUARD = 25; 
auto value = counter.load() 
if (value < GUARD) 
{ 

    auto expectValue = value; 
    auto newValue = expectValue + 1; 
    for(;;) 
    { 
     if (counter.cmpxchg(expect,new)) 
     {  
      break; // we did it! 
     } 
     newValue = expectValue + 1; 
     if (expectValue >= GUARD) 
     {  
      break; // someone else did it! 
     } 
     // someone else incremented, now we increment again 
     // cmpxchg updates expected with the new value 
    } 
    if (newValue <= GUARD) 
    { 
     // do the work you only want to do 25 times. 
    } 
} 
Verwandte Themen