Параллелизм: использование pthread_mutex для атомизации операции приращения - PullRequest
0 голосов
/ 22 октября 2018

Я сейчас читаю Операционные системы: три простых компонента , и я начинаю понимать логику параллелизма.В 26 «главе» мы получаем этот пример потоков и проблем, связанных с атомарностью:

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

static volatile int counter = 0;

// mythread()
// Simply adds 1 to counter repeatedly, in a loop
// No, this is not how you would add 10,000,000 to
// a counter, but it shows the problem nicely.

void *mythread(void *arg){
    printf("%s: begin\n", (char *) arg);
    int i;
    for (i = 0; i < 1e7; i++) {
        counter = counter + 1;
    }
    printf("%s: done\n", (char *) arg);
    return NULL;
}


// main()
// Just launches two threads (pthread_create)
// and then waits for them (pthread_join)

int main(int argc, char *argv[]) {
    pthread_t p1, p2;
    printf("main: begin (counter = %d)\n", counter);
    pthread_create(&p1, NULL, mythread, "A");
    pthread_create(&p2, NULL, mythread, "B");

    // join waits for the threads to finish
    pthread_join(p1, NULL);
    pthread_join(p2, NULL);
    printf("main: done with both (counter = %d)\n", counter);
    return 0;

}

И это показывает нам, что проблема заключается в том, что из-за условия гонки в приращении значение суммы меняется иредко бывает предполагаемым.

За пример, после компиляции:

gcc -g -o main page6.c -Wall -pthread

и запуска дважды, я получаю:

main: begin (counter = 0)
A: begin
B: begin
A: done
B: done
main: done with both (counter = 10263001)

и:

main: begin (counter = 0)
A: begin
B: begin
A: done
B: done
main: done with both (counter = 10600399)

Так что после прочтения о мьютексе я попробовал это небольшое изменение в коде в функции mythread ().

void *mythread(void *arg){
    pthread_mutex_t lock;
    int rc = pthread_mutex_init(&lock,NULL);
    assert(rc==0); //always check sucess

    printf("%s: begin\n", (char *) arg);
    int i;

    for (i = 0; i < 1e7; i++) {
        pthread_mutex_lock(&lock);
        counter = counter + 1;
        pthread_mutex_unlock(&lock);
    }

    printf("%s: done\n", (char *) arg);
    return NULL;
}

и после компиляции (таким же образом) и запуска, это занимает заметно больше времени (1-2 секунды).

Но результаты не лучше:

main: begin (counter = 0)
A: begin
B: begin
B: done
A: done
main: done with both (counter = 10019830)

и:

main: begin (counter = 0)
A: begin
B: begin
B: done
A: done
main: done with both (counter = 10008806)

Так почему же это не работает?Не должно ли это создать критический раздел в коде?Я что-то упускаю из виду?

1 Ответ

0 голосов
/ 22 октября 2018

Вы не должны иметь мьютекс в качестве локальной переменной в mythread, поскольку каждый поток получает свою собственную копию.Сделайте его глобальным и инициализируйте его в main.

...