Previous Lecture Lecture 17 Next Lecture

Lecture 17, Tue 12/04

Threads

Threads

Recall our discussion on processes

Threads and hardware

Concurrency

Example of creating a thread

#include <iostream>
#include <thread>
#include <unistd.h>
#include <string>

using namespace std;

void callback1(string val, int num) {
	cout << "----" << endl;
	cout << "Thread with str val: " << val << endl;
	cout << "num: " << num << endl;
	cout << "Thread id: " << this_thread::get_id() << endl;
	sleep(5);
	cout << "----" << endl;
}

int main() {
	std::thread t1(callback1, "one", 1); // add args to function here
	cout << "t1 id: " << t1.get_id() << endl;
	cout << "Done." << endl;

	return 0;
}
int main() {
	std::thread t1(callback1, "one", 1); // add args to function here
	cout << "t1 id: " << t1.get_id() << endl;

	// main thread waits for t1 to finish execution before resuming
	// its own execution
	//t1.join();
	
	// or main thread detaches t1 and does not wait for t1 to finish.
	// Since t1 is not detached, program will not terminate abnormally.
	//t1.detach();

	cout << "Done." << endl;

	return 0;
}

Example of a thead creating a thread

void callback2(bool create) {
	cout << "***" << endl;
	cout << "Starting thread: " << this_thread::get_id() << endl;
	sleep(5);

	if (create) {
		thread subt1(callback2, false);
		cout << "Creating thread: " << subt1.get_id() << endl;
		subt1.join();
	}
	cout << "Finishing thread: " << this_thread::get_id() << endl;
	cout << "***" << endl;
}

int main() {
	thread t2(callback2, true);
	cout << "main::t2 id: " << t2.get_id() << endl;
	t2.join();
	cout << "Done." << endl;
	return 0;
}

Race Conditions

Example of a Race Condition

# Makefile
bank:
	g++ -o main -std=c++11 main.cpp bank.cpp
-------
// Bank.h
//#include <mutex>

class Bank {
public:
	Bank();
	void deposit(double amount);
	double getBalance();
private:
	double totalBalance;
};
----
// Bank.cpp

#include "Bank.h"

Bank::Bank() {
	totalBalance = 0;
}

void Bank::deposit(double amount) {
	totalBalance += amount;
}

double Bank::getBalance() {
	return totalBalance;
}
----
#include <iostream>
#include <thread>
#include "Bank.h"

using namespace std;

void run(Bank* b) {
	for (int i = 0; i < 10000; i++) { b->deposit(10); }
}

int main() {
	Bank* bank = new Bank();
	thread t1(run, bank);
	thread t2(run, bank);
	t1.join();
	t2.join();
	cout << "---" << endl;
	cout << bank->getBalance() << endl;
	return 0;
}

Mutex (mutual exclusion)

Updated code using a mutex object

// Bank.h
#include <mutex>

class Bank {
public:
	Bank();
	void deposit(double amount);
	double getBalance();
private:
	double totalBalance;
	std::mutex mutex;
};
----
// Bank.cpp

#include "Bank.h"

Bank::Bank() {
	totalBalance = 0;
}

void Bank::deposit(double amount) {
	mutex.lock();
	totalBalance += amount;
	mutex.unlock();
}

double Bank::getBalance() {
	return totalBalance;
}
----
#include <iostream>
#include <thread>
#include "Bank.h"

using namespace std;

void run(Bank* b) {
	for (int i = 0; i < 10000; i++) { b->deposit(10); }
}

int main() {
	Bank* bank = new Bank();
	thread t1(run, bank);
	thread t2(run, bank);
	t1.join();
	t2.join();
	cout << "---" << endl;
	cout << bank->getBalance() << endl;
	return 0;
}

Deadlock

Deadlock