The practice of multithreading is to parallelise our programs by using multiple threads.

Operations

There are a few basic operations we should consider when multithreading by hand:

  • Creating threads — oftentimes we pass a function pointer to a creation function.
  • Joining threads — which block/wait the currently running function until the spawned thread returns.
    • Note that this isn’t strictly necessary, especially for long-lived threads, or for certain program use cases.

Tips

These are pulled from Operating Systems: Three Easy Steps. There may be some additions or removals here and there:

  • Keep it simple — any code that locks or signals between threads should be as simple as possible.
  • Minimise thread interactions — each interaction should be carefully thought out and constructed with well-known and understood approaches.
  • Initialise locks and condition variables — if we don’t do this, we’ll get code that might sometimes work and might not. In other words, undefined behaviour.
  • Check return codes — this is a general tip for system programming, but it is especially true here.
  • Check arguments and return values from threads — if we’re passing a reference or pointer to something allocated on the stack, we’re probably doing something wrong.
  • Each thread has its own stack — i.e., this is essentially private to the stack. We use the heap to share data between the stack.
  • Use condition variables to signal between threads — we use threading conditions, not simple flags. Otherwise, we run the risk of wasting CPU time.

Sub-pages

In C++

pretty much passing a function pointer

#include <thread>
int main () {
	thread workerThread (loadMapStreets); // starts workerThread
	loadMapOSM(); // main thread continues and runs loadmaposm
	workerThread.join(); // main thread waits for workerThread to finish
}