Gedeelde teller geeft andere waarde met spins

Ik heb de volgende code:

#include 
#include 
#define THREAD_CNT 10
#define ITER 100
#define PRINT 1

int lock;
unsigned long long int counter;

void spin_lock(int *p) {
    while(!__sync_bool_compare_and_swap(p, 0, 1));
}

void spin_unlock(int volatile *p) {
    asm volatile ("");
    *p = 0;
}

void *exerciser(void *arg) {
    unsigned long long int i;
    int id = (int)arg;
    for(i = 0; i < ITER; i++) {
        spin_lock(&lock);
        counter = counter + 1;
        if(PRINT) {
            printf("%d: Incrementing counter: %llu -> %llu\n", id, counter-1, counter);
        }
        spin_unlock(&lock);
    }
    pthread_exit(NULL);
}

int main(int argc, char *argv[]) {
    pthread_t thread[THREAD_CNT];
    counter = 0;
    int i;
    for(i = 0; i < THREAD_CNT; i++) {
        pthread_create(&thread[i], NULL, exerciser, (void *) i);
    }
    for(i = 0; i < THREAD_CNT; i++) {
        pthread_join(thread[i], NULL);
    }
    printf("Sum: %llu\n", counter);
    printf("Main: Program completed. Exiting.\n");
    pthread_exit(NULL);
}

Wanneer PRINT is gedefinieerd als 1, krijg ik aan het einde de juiste tellerwaarde:

7: Incrementing counter: 996 -> 997
7: Incrementing counter: 997 -> 998
7: Incrementing counter: 998 -> 999
7: Incrementing counter: 999 -> 1000
Sum: 1000
Main: Program completed. Exiting.

Als ik PRINT 0 maak, krijg ik het volgende (meerdere runs):

$ ./a.out
Sum: 991
Main: Program completed. Exiting.
$ ./a.out 
Sum: 1000
Main: Program completed. Exiting.
$ ./a.out 
Sum: 962
Main: Program completed. Exiting.
$ ./a.out 
Sum: 938
Main: Program completed. Exiting.

Enig inzicht wat er aan de hand is? Waarom is het zo dat wanneer ik de printinstructie heb ingeschakeld mijn resultaten (consistent) correct zijn, maar ik het uitschakel en mijn teller de doelwaarde niet haalt? Ik heb pthread aardig gebruikt, maar nog niet veel ervaring met het rechtstreeks gebruiken van spinlocks.

Alle hulp of feedback wordt op prijs gesteld.

0

1 antwoord

Uw vergrendelingsmethoden zijn eigenlijk niets aan het doen: aangezien argumenten worden doorgegeven op basis van waarde, test u feitelijk niet de waarde van uw vergrendeling globaal. U wijzigt alleen de waarde van de kopie van de variabele die uw functie krijgt.

If instead your spin_lock/spin_unlock methods took a pointer to the integer (i.e. &lock) to use then your code should work.

Uw dragende printf helpt waarschijnlijk door enige onbedoelde synchonisatie te veroorzaken, omdat printf geacht wordt te kunnen werken met threadsafe.

1
toegevoegd
Oh man, bedankt. Ik heb mijn bericht bijgewerkt om dat te weerspiegelen.
toegevoegd de auteur aqua, de bron