I'm trying to understand concurrency and using locks better, but this dummy example I made is throwing me off:
int i = 0;
void foo() {
int n = i;
i = i + 1;
printf("foo: %d\n", n);
}
void boo() {
int n = i;
i = i + 1;
printf("boo: %d\n", n);
}
int main(int argc, char* argv[]) {
pthread_t p1, p2;
pthread_create(&p1, NULL, (void*) foo, NULL);
pthread_create(&p2, NULL, (void*) boo, NULL);
// wait for threads to finish
pthread_join(p1, NULL);
pthread_join(p2, NULL);
// final print
printf("main: %d\n", i);
return 0;
}
If I understand correctly, the i = i + 1; in both foo() and bar() can cause some unexpected behaviour. One unexpected behaviour is that we'll get both "foo: 0" and "bar: 0" since it's possible that a context switch happened right before the i = i + 1; and so n is always 0. I think the expected behaviour is that "foo: 0" "bar: 1" or "bar: 0" "foo: 1" (please correct me if I'm wrong).
To fix this, I added locks:
int i = 0;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
void foo() {
int n = i;
i = i + 1;
printf("foo: %d\n", n);
}
void boo() {
int n = i;
i = i + 1;
printf("boo: %d\n", n);
}
int main(int argc, char* argv[]) {
pthread_t p1, p2;
printf("Locking foo\n");
pthread_mutex_lock(&lock);
printf("Locked foo\n");
pthread_create(&p1, NULL, (void*) foo, NULL);
pthread_mutex_unlock(&lock);
printf("Unlocked foo\n");
printf("Locking boo\n");
pthread_mutex_lock(&lock);
printf("Locked boo\n");
pthread_create(&p2, NULL, (void*) boo, NULL);
pthread_mutex_unlock(&lock);
printf("Unlocked boo\n");
// wait for threads to finish
pthread_join(p1, NULL);
pthread_join(p2, NULL);
// final print
printf("main: %d\n", i);
return 0;
}
I think this would fix the unexpected results, but I got a surprising output when I ran this:
Locking foo
Locked foo
Unlocked foo
Locking boo
Locked boo
foo: 0
Unlocked boo
boo: 1
main: 2
It looks like the program locked the first thread that calls foo() and then immediately unlocked it without actually executing the printf? It then goes on to locking the thread that calls boo() and does weird things out of order. Can someone explain this behaviour? I would of thought the output would have looked like:
Locking foo
Locked foo
foo: 0
Unlocked foo
Locking boo
Locked boo
boo: 1
Unlocked boo
main: 2