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
- Atomics
- Mutex
- Race condition
- Per language
In C++
pretty much passing a function pointer