[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] | [ Search: ] |
The threading library is a collection of routines and classes for creating multithreaded and thread safe code and programs.
Remember that just because you can make something multithreaded and threading safe does not mean you should do so, or doing so is a good idea and will give you a lot of benefit. Use with care, when appropriate.
All threading related classes and operations are in the CS::Threading
namespace.
Atomic operations are the basic and smallest operations behind threading safe code. Atomic in this context means that they are guaranteed to complete with a deterministic result in relation to other threads and even cores doing other atomic operations on the same data.
Atomic operations in Crystal Space is found in CS::Threading::AtomicOperations
class in ‘csutil/threading/atomicops.h’. There are a few different atomic
operations, namely:
Operation | Function | Description |
Set | | Atomically set the content of a pointer or 32 bit integer variable and return the value it had before setting it. |
Read | | Read content of pointer or 32 bit integer variable. |
Compare and Set (CAS) | | Atomically compare current content of pointer or 32 bit integer variable and if it is equal to a given value exchange it. Returns the value of the variable before comparison. Atomic CAS operations are among the most used and most useful when it comes to building locking primitives or lock-free data structures. |
Increment | | Atomically increment 32 bit integer variable and return its value prior to incrementation. |
Decrement | | Atomically decrement 32 bit integer variable and return its value prior to decrementation. |
A thread is the smallest unit of execution when looking at multithreaded execution. Each thread is a "lane" of sequential execution and by having multiple threads executing at the same time you get the effect of simultaneous execution.
As this concept of simultaneous execution can be a bit confusing at first there are two important things to remember
A more important result of this is that you cannot rely on or even know when one thread is interrupted to let another one execute. This means that whenever you operate on data shared between two or more threads you must use one of the syncronization primitives or atomic operations.
To create a thread you first need to implement the interface
CS::Threading::Runnable
and instance it. Then create an instance of
CS::Threading::Thread
to start a new thread executing the runnable
instance.
Use the Start()
and Stop()
methods of the thread to control its
execution and Wait()
to wait until it finishes execution.
Here is an example of a simple thread and creation
#include "crystalspace.h" using namespace CS::Threading; class MyRunner : public Runnable { void Run () { int i = 0; while (i < 100) { // Wait for a while csSleep (1000); // Print a message. Notice that this is potentially not thread safe! csPrintf ("Thread printing\n"); } } }; int main (int argc, char* argv[]) { // Instance runnable and thread csRef<Runnable> r; r.AttachNew (new MyRunner); csRef<Thread> thread; thread.AttachNew (new Thread (r)); // Start the thread thread->Start (); // Wait for a while to let it run csSleep (500); // Wait for the thread to finish running thread->Wait (); } |
As you can see in this example it is not doing anything useful while the thread is running. In a real application you would do something useful while the thread is for example waiting for IO.
The threading library provides two different thread syncronization primitives. The two works differently and do not have the same purpose. Which primitive to use depends on the situation at hand.
Type | Class | Usage |
Mutex | | Mutually exclusive access to a shared resource. N threads share a common buffer and only one thread should be allowed to modify it at a time. |
Condition | | Signal that a condition is fullfilled. One or more threads wait for a condition to be signaled by a source. |
To note is that a condition always is used together with a mutex, which also should be shared between the threads.
The example below shows how a mutex and a condition can be used to distribute
work units between two threads. Note, this sample works but is not fully complete.
For a better implementation, look at CS::Threading::ThreadedJobQueue
#include "crystalspace.h" using namespace CS::Threading; // Shared data csFIFO<int> queue; // Mutex to protect it Mutex queueMutex; Condition newItemInQueue; class MyRunner : Runnable { void Run () { while (true) { // Get an item from queue int myData; { // Make sure we lock the queue before trying to access it MutexScopedLock lock (queueMutex); // Wait until we have an item while (queue.Length() == 0) newItemInQueue.Wait (queueMutex); // When we get here we know that: // 1. newItemInQueue have been signaled // 2. We have the queueMutex locked // 3. The queue is not empty // Get the data myData = queue.PopTop (); } // Process it csPrintf ("Processing data: %d\n", myData); csSleep (400); } } }; int main (int argc, char* argv[]) { // Instance runnable and thread csRef<Runnable> r; r.AttachNew (new MyRunner); csRef<Thread> thread; thread.AttachNew (new Thread (r)); // Start the thread thread->Start (); // Give it some data for (int i = 0; i < 100; ++i) { // Prepare the data int myData = (i * 10 + 31) % 47; { //Lock the queue and insert our data MutexScopedLock lock (queueMutex); queue.Push (myData); } // Notify one waiting threads, let them compete over who takes it newItemInQueue.NotifyOne (); } ... } |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
This document was generated using texi2html 1.76.