Intro to concurrency in C++

Posted on Aug 17, 2024 DRAFT

Starting threads

The C++11 standard gave us threads in the C++ standard library. Starting a new thread is pretty straight-forward by constructing a thread object, giving the entry function as an argument. Execution will start immediately.

#include <thread>

void do_some_work();

int main() {
	std::thread my_thread(do_some_work);
}

The thread constructor also works with any callable type:

class A {
public:
	void operator() () const {
		...
	}
}

int main() {
	A foo = A();
	std::thread my_thread(foo);
}

It can also take a lambda:

std::thread my_thread([] {
	do_something();
	do_something_else();
})

Waiting and detaching

Note that while our thread is running, main is also executing. After main ends, any running threads are forcefully terminated, so it would be a good idea for them to finish before ending execution in main.

Waiting on a thread is done by calling join on the thread object:

#include <thread>

void do_some_work();

int main() {
	std::thread my_thread(do_some_work);
	
	...
	
	my_thread.join();
}

A better approach to joining threads is to use RAII. We make sure our thread object is automatically joined once it goes out of scope.

class thread_gurad {
	std::thread& t
public:
	~thread_guard() {
		if (t.joinable()) { t.join(); }
	}
  // Make sure to delete copy and copy-assignment constructors
}

Detaching threads is done by calling detach on the thread object. They are also called daemons and will run even after main ends execution.

References

  • Williams, Anthony. C++ Concurrency in Action: Practical Multithreading. Shelter Island, NY: Manning, 2012.