cthreads.c
// Sample code that demonstrates the use of pthread_create and pthread_join.
// Multiple threads print out a sequence of numbers using printf.
// Although created in order, execution order will vary.
// For small LOOP_NUM, likely won't get interleaving of threads.
//
// Remember to compile with -pthread
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#define NUM_THREADS 4
#define LOOP_NUM 10
// print out a sequence of LOOP_NUM numbers with thread identifying number
void *thread_main(void *arg) {
int *num = (int *) arg; // read void * argument as intended
for (int i = 0; i < LOOP_NUM; i++) {
printf("[thread %d] %d\n", *num, i);
}
free(num); // child thread cleans up the memory allocated for its argument
return NULL; // return type is a pointer
}
int main(int argc, char** argv) {
pthread_t thds[NUM_THREADS]; // array of thread ids
// create threads to run thread_main()
for (int i = 0; i < NUM_THREADS; i++) {
int *num = (int*)malloc(sizeof(int));
*num = i;
if (pthread_create(&thds[i], NULL, &thread_main, num) != 0) {
fprintf(stderr, "pthread_create failed\n");
}
}
// wait for all child threads to finish
// (children may terminate out of order, but cleans up in order)
for (int i = 0; i < NUM_THREADS; i++) {
if (pthread_join(thds[i], NULL) != 0) {
fprintf(stderr, "pthread_join failed\n");
}
}
return EXIT_SUCCESS;
}
exit_thread.c
// Sample code that demonstrates the use of pthread_create and pthread_join.
// Multiple threads print out a sequence of numbers using printf.
// Although created in order, execution order will vary.
// For small LOOP_NUM, likely won't get interleaving of threads.
//
// Remember to compile with -pthread
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#define NUM_THREADS 4
#define LOOP_NUM 10
// print out a sequence of LOOP_NUM numbers with thread identifying number
void *thread_main(void *arg) {
int *num = (int *) arg; // read void * argument as intended
for (int i = 0; i < LOOP_NUM; i++) {
printf("[thread %d] %d\n", *num, i);
}
free(num); // child thread cleans up the memory allocated for its argument
return NULL; // return type is a pointer
}
int main(int argc, char** argv) {
pthread_t thds[NUM_THREADS]; // array of thread ids
// create threads to run thread_main()
for (int i = 0; i < NUM_THREADS; i++) {
int *num = (int*)malloc(sizeof(int));
*num = i;
if (pthread_create(&thds[i], NULL, &thread_main, num) != 0) {
fprintf(stderr, "pthread_create failed\n");
}
}
// No need to join every child since we don't need to do anything after
// joining them. instead, detach them and just call pthread_exit().
for (int i = 0; i < NUM_THREADS; i++) {
if (pthread_detach(thds[i]) != 0) {
fprintf(stderr, "pthread_detach failed\n");
}
}
// Note that we can't use return otherwise the entire process will be
// cleaned up and threads may not finish.
pthread_exit(NULL);
}
pthreads.cc
// C++ version of cthreads.c
// Sample code that demonstrates the use of pthread_create and pthread_join.
// Multiple threads print out a sequence of numbers using cout stream.
// Although created in order, execution order will vary.
// Because of increased complexity in using streams, output may get fragmented.
//
// Remember to compile with -pthread
#include <pthread.h>
#include <iostream>
using std::cout;
using std::cerr;
using std::endl;
const int NUM_THREADS = 4;
const int LOOP_NUM = 10;
// print out a sequence of LOOP_NUM numbers with thread identifying number
void *thread_main(void *arg) {
int *num = reinterpret_cast<int*>(arg); // read void * argument as intended
for (int i = 0; i < LOOP_NUM; i++) {
cout << "[thread " << *num << "] " << i << endl;
}
delete num; // child thread cleans up the memory allocated for its argument
return nullptr; // return type is a pointer
}
int main(int argc, char** argv) {
pthread_t thds[NUM_THREADS]; // array of thread ids
// create threads to run thread_main()
for (int i = 0; i < NUM_THREADS; i++) {
int *num = new int(i);
if (pthread_create(&thds[i], nullptr, &thread_main, num) != 0) {
cerr << "pthread_create failed" << endl;
}
}
// wait for all child threads to finish
// (children may terminate out of order, but cleans up in order)
for (int i = 0; i < NUM_THREADS; i++) {
if (pthread_join(thds[i], nullptr) != 0) {
cerr << "pthread_join failed" << endl;
}
}
return EXIT_SUCCESS;
}
total.cc
// Sample code that demonstrates a data race between threads reading and writing
// from the same shared global variable in Static Data.
// Interleaving of reads and writes will sometimes cause printed sum to be less
// than expected.
//
// Remember to compile with -pthread
#include <pthread.h>
#include <iostream>
using std::cout;
using std::cerr;
using std::endl;
const int NUM_THREADS = 50;
const int LOOP_NUM = 10000;
static int sum_total = 0;
// increment sum_total LOOP_NUM times
void *thread_main(void *arg) {
for (int i = 0; i < LOOP_NUM; i++) {
sum_total++;
}
return nullptr; // return type is a pointer
}
int main(int argc, char** argv) {
pthread_t thds[NUM_THREADS]; // array of thread ids
// create threads to run thread_main()
for (int i = 0; i < NUM_THREADS; i++) {
if (pthread_create(&thds[i], nullptr, &thread_main, nullptr) != 0) {
cerr << "pthread_create failed" << endl;
}
}
// wait for all child threads to finish
// (children may terminate out of order, but cleans up in order)
for (int i = 0; i < NUM_THREADS; i++) {
if (pthread_join(thds[i], nullptr) != 0) {
cerr << "pthread_join failed" << endl;
}
}
// print out the final sum (expecting NUM_THREADS * LOOP_NUM)
cout << "Total: " << sum_total << endl;
return EXIT_SUCCESS;
}
total_locking.cc
// Sample code that demonstrates using pthread mutex synchronization.
// from the same shared global variable in Static Data.
// Interleaving of reads and writes will sometimes cause printed sum to be less
// than expected.
//
// Remember to compile with -pthread
#include <pthread.h>
#include <iostream>
using std::cout;
using std::cerr;
using std::endl;
const int NUM_THREADS = 50;
const int LOOP_NUM = 10000;
static int sum_total = 0;
static pthread_mutex_t sum_lock; // global, accessible by all threads
// increment sum_total LOOP_NUM times in an atomic fashion
void *thread_main(void *arg) {
for (int i = 0; i < LOOP_NUM; i++) {
pthread_mutex_lock(&sum_lock);
sum_total++;
pthread_mutex_unlock(&sum_lock);
}
return nullptr; // return type is a pointer
}
int main(int argc, char** argv) {
pthread_t thds[NUM_THREADS]; // array of thread ids
pthread_mutex_init(&sum_lock, nullptr); // initialize mutex to default
// create threads to run thread_main()
for (int i = 0; i < NUM_THREADS; i++) {
if (pthread_create(&thds[i], nullptr, &thread_main, nullptr) != 0) {
cerr << "pthread_create failed" << endl;
}
}
// wait for all child threads to finish
// (children may terminate out of order, but cleans up in order)
for (int i = 0; i < NUM_THREADS; i++) {
if (pthread_join(thds[i], nullptr) != 0) {
cerr << "pthread_join failed" << endl;
}
}
// print out the final sum (expecting NUM_THREADS * LOOP_NUM)
cout << "Total: " << sum_total << endl;
pthread_mutex_destroy(&sum_lock); // destroy the mutex to clean up
return EXIT_SUCCESS;
}
total_locking_better.cc
// Sample code that demonstrates using pthread mutex synchronization.
// from the same shared global variable in Static Data.
// Interleaving of reads and writes will sometimes cause printed sum to be less
// than expected.
//
// Remember to compile with -pthread
#include <pthread.h>
#include <iostream>
using std::cout;
using std::cerr;
using std::endl;
const int NUM_THREADS = 50;
const int LOOP_NUM = 10000;
static pthread_mutex_t sum_lock; // global, accessible by all threads
// NEW: struct to pass arguments to thread_main
struct thd_arg {
int *sum_ptr;
int num;
};
// increment sum_total LOOP_NUM times in an atomic fashion
void *thread_main(void *arg) {
// NEW: cast arguments back into struct
struct thd_arg *a = reinterpret_cast<struct thd_arg *>(arg);
int local_sum = 0;
for (int i = 0; i < a->num; i++) {
local_sum++;
}
pthread_mutex_lock(&sum_lock);
*a->sum_ptr += local_sum;
pthread_mutex_unlock(&sum_lock);
// NEW: delete dynamically-allocated struct
delete a;
return nullptr; // return type is a pointer
}
int main(int argc, char** argv) {
// NEW: made sum_total a local variable
int sum_total = 0;
pthread_t thds[NUM_THREADS]; // array of thread ids
pthread_mutex_init(&sum_lock, nullptr); // initialize mutex to default
// create threads to run thread_main()
for (int i = 0; i < NUM_THREADS; i++) {
struct thd_arg *args = new struct thd_arg;
args->sum_ptr = &sum_total;
args->num = LOOP_NUM;
if (pthread_create(&thds[i], nullptr, &thread_main, args) != 0) {
cerr << "pthread_create failed" << endl;
}
}
// wait for all child threads to finish
// (children may terminate out of order, but cleans up in order)
for (int i = 0; i < NUM_THREADS; i++) {
if (pthread_join(thds[i], nullptr) != 0) {
cerr << "pthread_join failed" << endl;
}
}
// print out the final sum (expecting NUM_THREADS * LOOP_NUM)
cout << "Total: " << sum_total << endl;
pthread_mutex_destroy(&sum_lock); // destroy the mutex to clean up
return EXIT_SUCCESS;
}
Makefile
CXX = g++ --std=c++11
CX = gcc --std=c11
FLAGS = -g -Wall -Og -pthread
BINARIES = cthreads pthreads exit_thread total total_locking total_locking_better
.PHONY: all, clean
all: $(BINARIES)
cthreads: cthreads.c
$(CX) $(FLAGS) -o $@ $<
exit_thread: exit_thread.c
$(CX) $(FLAGS) -o $@ $<
pthreads: pthreads.cc
$(CXX) $(FLAGS) -o $@ $<
total: total.cc
$(CXX) $(FLAGS) -o $@ $<
total_locking: total_locking.cc
$(CXX) $(FLAGS) -o $@ $<
total_locking_better: total_locking_better.cc
$(CXX) $(FLAGS) -o $@ $<
clean:
rm -rf $(BINARIES)