Section 8: Multithreading #

Question 1: Racy Programs #

In the following, we will look at a set of C++ programs that increment variables in multiple threads. For each of the programs, does it contain a race condition or not?

QUESTION 1A: Does this program contain a race condition? What does it print?

#include <iostream>
#include <thread>

void increment(int n) {
    for (int i = 0; i < 1000000; i++) {
        n++;
    }
    printf("n: %d\n", n);
}

int main() {
    int n = 0;
    std::thread t1(increment, n);
    std::thread t2(increment, n);
    std::thread t3(increment, n);

    t3.join();
    t2.join();
    t1.join();

    printf("n: %d\n", n);
}

QUESTION 1B: Does this program contain a race condition? What does it print?

#include <iostream>
#include <thread>

int n = 0;

void increment(int* n) {
    for (int i = 0; i < 1000000; i++) {
        *n = *n + 1;
    }
    printf("n: %d\n", *n);
}

int main() {
    std::thread t1(increment, &n);
    std::thread t2(increment, &n);
    std::thread t3(increment, &n);

    t3.join();
    t2.join();
    t1.join();

    printf("n: %d\n", n);
}

QUESTION 1C: Does this program contain a race condition? What does it print?

#include <iostream>
#include <thread>

void increment(int* n) {
    for (int i = 0; i < 1000000; i++) {
        *n = *n + 1;
    }
    printf("n: %d\n", *n);
}

int main() {
    int* n = new int;
    *n = 0;

    std::thread t1(increment, n);
    std::thread t2(increment, n);
    std::thread t3(increment, n);

    t3.join();
    t2.join();
    t1.join();

    printf("n: %d\n", *n);
    delete(n);
}

QUESTION 1D: Does this program contain a race condition? What does it print?

#include <iostream>
#include <thread>

void increment_three(int n[3]) {
    for (int i = 0; i < 1000000; i++) {
        n[0]++;
        n[1]++;
        n[2]++;
    }

    printf("n0: %d\n", n[0]);
    printf("n1: %d\n", n[1]);
    printf("n2: %d\n", n[2]);
}

int main() {
    int n[3];
    n[0] = 0;
    n[1] = 0;
    n[2] = 0;

    std::thread t1(increment_three, n);
    std::thread t2(increment_three, n);
    std::thread t3(increment_three, n);

    t3.join();
    t2.join();
    t1.join();

    printf("n0: %d\n", n[0]);
    printf("n1: %d\n", n[1]);
    printf("n2: %d\n", n[2]);
}

QUESTION 1E: Does this program contain a race condition? What does it print?

#include <iostream>
#include <thread>

void increment(int thread_id, int* n) {
    for (int i = 0; i < 1000000; i++) {
        *n = *n + 1;
    }

    printf("n[%d]: %d\n", thread_id, *n);
}

int main() {
    int n[3];
    n[0] = 0;
    n[1] = 0;
    n[2] = 0;

    std::thread t1(increment, 0, &n[0]);
    std::thread t2(increment, 1, &n[1]);
    std::thread t3(increment, 2, &n[2]);

    t3.join();
    t2.join();
    t1.join();

    printf("sum(n): %d\n", n[0] + n[1] + n[2]);
}

(EXTRA) QUESTION 1F: Does this program contain a race condition? What does it print?

Note: This example uses std::atomic, which we will likely not have covered in class by the time we do section. If there's time, though, consider: what might this type mean?
#include <iostream>
#include <thread>
#include <atomic>

std::atomic<int> n(0);

void increment(std::atomic<int>* n) {
    for (int i = 0; i < 1000000; i++) {
        atomic_fetch_add(n, 1);
    }
    printf("n: %d\n", n->load());
}

int main() {
    std::thread t1(increment, &n);
    std::thread t2(increment, &n);
    std::thread t3(increment, &n);

    t3.join();
    t2.join();
    t1.join();

    printf("n: %d\n", n.load());
}

Creative Commons Licence This work is licensed under a Creative Commons Attribution 4.0 International License.