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());
}