c ++ - Hvorfor forekommer mine atomoperationer i en uventet rækkefølge?

Indlæg af Hanne Mølgaard Plasc

Problem



Jeg forstår ikke, hvorfor dette sker. Tag det følgende stykke psuedo kode:


volatile unsigned long count = 0;
volatile unsigned long sum = 0;

void ThreadFunction() {
    InterlockedIncrement(&count);
    InterlockedExchangeAdd(&sum, rand());
}

int main() {
    for (int i = 0; i < 10; ++i) {
        // This is the problematic instruction
        InterlockedExchange(&count, 0);
        InterlockedExchange(&sum, 0);

        std::vector<boost::thread> threads(i);
        for (int j = 0; j < i; ++j)
            threads[j] = boost::thread(ThreadFunction);

        while (count != i)
            Sleep(0);
    }
}


På den første runde af programmet, når i = 0, forekommer atomudvekslingen på sum fra hovedtråden normalt efter den genfandt tråd. Operationerne på count forekommer altid i den rigtige rækkefølge.


Dette sker kun en gang; det gør operationerne i den rigtige rækkefølge for resten af ​​løkken. Det sker ikke altid, men det gør det normalt. Hvis jeg bryder ind i debuggeren eller sover før den atomvise tilføjelse, vil instruktionerne blive udført i den rigtige rækkefølge.


Uanset hvad er resultatet, at værdien, der er skrevet af tråden, er erstattet af den 0, der skulle have fundet sted, før tråden blev lanceret.


Hvorfor sker det her? Hvorfor kan jeg ikke stole på processoren for at fuldføre de atominstruktioner, jeg gav den i den rækkefølge, jeg gav dem i? Er det ikke en atomoperation, der indebærer en hukommelsesbarriere for at forhindre omplacering?


Sidebemærkning, dette sker med Visual Studio 2010. Jeg har ikke andre versioner til at teste med.

Bedste reference


En atomoperation er atomisk i den forstand, at den ikke kan afbrydes. Den vil heller ikke udføre eller fuldføre uden at en anden tråd får et indblik.


Det betyder ikke, at 20 atomiske operationer altid vil forekomme i samme rækkefølge, at de stadig er underkastet skygge af multi-threading.


Afhængigt af hvor hurtigt dine tråde starter, kan de blive sekventeret som main, thrd1, main, thrd2, main, thrd3, ... eller main, main, main, main, thrd1, main, main, thrd2, ... eller en hvilken som helst anden kombination (selv om jeg er temmelig sikker main altid ville være først, fordi dens atomoperation er inden der startes tråde).