読者です 読者をやめる 読者になる 読者になる

thread / mutex

引き続き MIT Open CoursewareのPractical Programming in C に取り組み中。本日はLecture13のrace conditionについて。

#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>

unsigned int cnt = 0;

void *count(void *arg) 
{
    int i;
    for ( i = 0; i < 100000; i++ ) {
        cnt++;
    }
    return NULL;
}

int main(int argc, char* argv[])
{
    pthread_t tids[4];
    int i;
    
    for ( i = 0; i < 4; i++ ) 
        pthread_create(&tids[i], NULL, count, NULL);
    for ( i = 0; i < 4; i++ ) 
        pthread_join(tids[i], NULL);

    printf("%d\n", cnt);
    return 0;
}
$ ./a.out 
315920

$ ./a.out 
257619

100000 x 4 = 400000 になることを期待したいが、実際には違う。

これは、cnt++ がアセンブリレベルでは

  1. load cnt into a register
  2. increment value in register
  3. save new register value as new cnt

という風に分解され、アトミックに実行できないためである。

対処するにはmutexを利用する。

unsigned int cnt = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

void *count(void *arg) 
{
    int i;
    for ( i = 0; i < 100000; i++ ) {
        pthread_mutex_lock(&mutex);
        cnt++;
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}

int main(int argc, char* argv[])
{
    pthread_t tids[4];
    int i;
    
    for ( i = 0; i < 4; i++ ) 
        pthread_create(&tids[i], NULL, count, NULL);
    for ( i = 0; i < 4; i++ ) 
        pthread_join(tids[i], NULL);

    printf("%d\n", cnt);
    pthread_mutex_destroy(&mutex);
    return 0;
}