Threads
Spice supports multithreading. It is very easy to use and very lightweight. Under the hood Spice uses posix pthreads to realize multi-threading.
Usage¶
A simple worker thread can be spawned like this:
| Spice | |
|---|---|
Please always add the [[async]] attribute to the thread routine. This will ensure, that the thread routine is executed in a
thread-safe manner. The run() method will start the thread. The thread will run until the thread routine returns.
To join a thread to the main thread, use the join() method:
| Spice | |
|---|---|
To get the ID of a thread, use the getId() method. This only works, if run() was already called on the thread object.
To get the ID of the current thread (i.e. within the thread routine), you can call the static getThreadId() function:
| Spice | |
|---|---|
Thread pools¶
Spice offers thread pools out of the box via the std/os/threadpool module. A thread pool is a collection of worker threads of
a fixed size. The threads are spawned when the thread pool is created and are kept alive until the thread pool is destroyed.
The threads are idle until a task is enqueued to the thread pool. The thread pool then assigns the task to one of the workers.
The thread pool can be used like this:
To pause the thread pool, use the pause() method. This will pause all worker threads. To resume the thread pool, use the
resume() method. This will resume all worker threads. To stop the thread pool, use the stop() method. The worker threads will
continue the current task and then exit. You can start the thread pool again by calling the start() method at any time.
To wait for all tasks to finish, use the join() method. This will block the current thread until all tasks in the thread pool
are finished.
Mutexes¶
When multiple threads share state, you need a way to make sure that only one thread modifies it at a time.
The std/os/mutex module provides a Mutex type for this. It is backed by a POSIX pthread_mutex_t under the hood,
so calls to acquire() block (the OS scheduler parks the thread) until the mutex becomes available, rather than
busy-waiting on the CPU.
Basic usage¶
| Spice | |
|---|---|
The mutex initializes itself when constructed and destroys the underlying pthread handle in its destructor —
no explicit init/destroy calls are required.
If you only want to take the mutex when it is immediately available, use tryAcquire():
| Spice | |
|---|---|
LockGuard (RAII)¶
Forgetting to call release() — for example because an early return or a panic skips the call — leaves the
mutex permanently locked. The LockGuard type avoids that: it acquires the mutex in its constructor and releases
it in its destructor, so the mutex is freed when the guard goes out of scope, no matter how the scope exits.
| Spice | |
|---|---|
This is the recommended way to use a mutex unless you really need fine-grained control over when the lock is released.
Sharing a mutex across threads¶
A Mutex owns its underlying pthread handle, so you should not pass it by value across threads — always share it
by reference, typically by placing it inside a struct whose pointer the thread routine captures.
Because both threads increment this.value under the same LockGuard, no updates are lost. Without the mutex,
the read-modify-write of this.value++ would race and the final count would be less than 20000.