c ++ - simpelt tilpasset mutex fejler

Indlæg af Hanne Mølgaard Plasc

Problem



Kan du få vist fejlen i koden? billetter slutter gå under 0 forårsager lange boder.


struct SContext {
    volatile unsigned long* mutex;
    volatile long* ticket;
    volatile bool* done;
};

static unsigned int MyThreadFunc(SContext* ctxt) {

    // -- keep going until we signal for thread to close
    while(*ctxt->done == false) {

        while(*ctxt->ticket) { // while we have tickets waiting
            unsigned int lockedaquired = 0;
            do {
                if(*ctxt->mutex == 0) { // only try if someone doesn't have mutex locked
                    // -- if the compare and swap doesn't work then the function returns
                    // -- the value it expects
                    lockedaquired = InterlockedCompareExchange(ctxt->mutex, 1, 0);
                }
            } while(lockedaquired !=  0); // loop while we didn't aquire lock
            // -- enter critical section

            // -- grab a ticket
            if(*ctxt->ticket > 0);
                     (*ctxt->ticket)--;

            // -- exit critical section
            *ctxt->mutex = 0; // release lock
        }
     }

     return 0;
}


Opkaldsfunktion, der venter på, at tråde bliver færdige


    for(unsigned int loops = 0; loops < eLoopCount; ++loops) {
        *ctxt.ticket = eNumThreads; // let the threads start!

        // -- wait for threads to finish
        while(*ctxt.ticket != 0)
            ; 
    }
    done = true;


REDIGERE:


Svaret på dette spørgsmål er simpelt, og efter at jeg har brugt tiden til at trimme ned eksemplet for at sende en forenklet version, finder jeg straks svaret, efter jeg har stillet spørgsmålet. Suk..


Jeg initialiserer lockaquired til 0. Derefter som optimering for ikke at optage busbåndmedium, gør jeg ikke CAS, hvis mutexen er taget.


Desværre, i det tilfælde hvor låsen er taget, vil løkken løbe den anden tråd igennem!


Undskyld for det ekstra spørgsmål. Jeg troede, at jeg ikke forstod windows lavniveau synkroniserings primitiver, men jeg havde lige en simpel fejl.

Bedste reference


Jeg ser et andet løb i din kode: En tråd kan forårsage *ctxt.ticket at ramme 0, så moderløkken kan gå tilbage og genindstille *ctxt.ticket = eNumThreads uden at holde *ctxt.mutex. Nogle andre tråd kan allerede nu holde mutexen (faktisk det gør det sikkert) og operere på *ctxt.ticket. For dit forenklede eksempel forhindrer det kun 'partier' fra at være adskilt fra hinanden, men hvis du havde mere kompleks initialisering (som i mere kompleks end et enkelt ord skrive) øverst på loops sløjfen, kan du se mærkelig adfærd.

Andre referencer 1


Jeg skrev en fejl, hvor jeg troede, det var et legitimt multithreaded problem, men det var virkelig bare dårlig logik. Jeg løste fejlen, så snart jeg postede. Her er problemet linjer og svar


unsigned int lockedaquired = 0;


Jeg initialiserede lockaquired til 0 og derefter efter at jeg tilføjede en if-sætning til at springe over den dyre drift af at gøre en CAS. Denne optimering har fået det til at falde ud af while loop og ind i den kritiske sektion. Ændring af koden til


unsigned int lockedaquired = 1;


Løser problemet. Der er et andet skjult problem i koden, som jeg fandt så godt (jeg burde ikke kodes sent om aftenen længere). Nogen bemærker semikolonet efter if-sætningen i den kritiske sektion? Sigh ...


if(*ctxt->ticket > 0);
    (*ctxt->ticket)--;


Det burde være


if(*ctxt->ticket > 0)


Ben Jackson påpegede også, at en tråd sandsynligvis vil være inde i det kritiske afsnit, når vi nulstiller billetten til eNumThreads. Selvom dette er helt fint i denne prøvekode, hvis du skulle anvende det på et problem, hvor du skulle gøre flere operationer, er det muligvis ikke sikkert, fordi tråden ikke løber i låsen, så husk, hvis du anvender dette på din kode.


En endelig note, hvis nogen beslutter at bruge denne kode til deres egen implementering af en mutex, skal du huske at din hoveddriver tråd går i tomgang. Hvis du laver en stor operation i den kritiske sektion, der tager tid, og din billetælling er høj overveje at give din tråd for at lade anden software gøre brug af CPU'en, mens den venter. Overvej også at bruge en spin lås, hvis den kritiske sektion er stor.


tak skal du have