Jake
Jake

Reputation: 143

pthreads in c coming back with unexpected results

I'm using 2 unsynchronized threads to increment a global volatile int from 0 to 10000000. As expected, the int sometimes ends up at 10000001.

However, I'm also keeping count of how many times both threads execute their increment operations with a thread-specific local variable, and that one overshoots massively. Here's the code:

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

volatile int x = 0;


void* incThread(void* x) {
    int* y;
    y = malloc(sizeof(int));
    *y = 0;

    printf("tstart\n");

    while(*((int*)x) < 10000000) {
        *y = *y + 1;
        *((int*)x) = *((int*)x) + 1; 
        if(*y % 1000000 == 0) {
            printf(">thread at %i\n", *y));
        }
    }

    printf("tend\n");

    pthread_exit(y);
}



int main(int argc, char* argv[]) {

    pthread_t thread1;
    pthread_t thread2;

    volatile int* xp;
    xp = &x;

    int* ret1;
    int* ret2;


    printf("\n\n\nTHREAD LAUNCH PROGRAM\n");
    printf("-------------------------------------\n");
    printf("I'll launch two threads.\n");
    printf("Both will try incrementing the global value x to 10000000 before exiting.\n\n");


    pthread_create(&thread1, NULL, incThread, (void*)xp);
    pthread_create(&thread2, NULL, incThread, (void*)xp);
    pthread_join(thread1, (void**) &ret1);
    pthread_join(thread2, (void**) &ret2);


    printf("   Thread01 exited after %i loops.\n", *ret1); 
    printf("   Thread02 exited after %i loops.\n", *ret2);
    printf("                        --------\n");
    printf("                     => %i total\n", ((*ret1)+(*ret2)));
    printf("\n");
    printf("x ended up at %i.\n", x);
    printf("\n");

    return 0;
}

So, running this results in it printing out zany results for the threads' iteration counters (int y in incThread()); for example, Thread01's y = 5801001 and Thread02's y = 5456675 for a total of more than 112% the expected value, 10000000. Meanwhile, x itself ends up at 10000000 or one higher, as expected.

What gives? How can the iteration counts end up this high?

OS Info and what I thought should be happening: The virtual debian 7.1 this whole thing is running on has its affinity set to one CPU core. I'd expect the virtual OS to open 3 threads in the program's process. Then, as it's iteratively switching from process to process as part of its regular execution cycle, it should also keep switching through every processes threads (main thread and custom threads 1&2 in this one's case) for as long as its focusing on that specific process.

So, there's a main thread launching t1 and t2, then waits for thread1 to finish, as soon as that's done it waits for thread2 to finish, then continues printing the results. But as far as I understand, none of this explains how y can deviate from x by this much.

Upvotes: 0

Views: 85

Answers (3)

Marian
Marian

Reputation: 7472

I just want to enhance the answer of @chickenpie. The following scenario can happen easily:

t1: read x value (x is 5)
t2: read x value (x is 5)
t2: increment and store x (x is 6)
t2: increment and store x (x is 7)
... say 10 times
t2: increment and store x (x is 17)
t1: increment and store x (x is 6)

This can happen when the first thread is switched from one physical core to another or when it is interrupted to perform some other task.

Upvotes: 1

Andrey
Andrey

Reputation: 60065

This is typical race condition. y = y + 1 is not atomic and consists of 3 operations: read, increment, write. After read operation there can be thread switch and other thread will read same value, increment and write. Then other one will continue execution and repeat the same operations for same values. So increment happened 2 times but result will be only one increment.

Upvotes: 0

chickenpie
chickenpie

Reputation: 133

This happens because it is possible for x to be incremented once per iteration of both threads. For example,

t1: read x value (x is 5)
t2: read x value (x is 5)
t1: increment and store x (x is 6)
t2: increment and store x (x is 6)

So both threads completed an iteration but the value of x was just incremented once. It is theoretically possible for EACH of your threads to run up to 10000000 times.

Upvotes: 3

Related Questions