threadExample.cpp

Example showing ARIA's cross-platform threading tools.

ARIA provides some tools for writing multithreaded programs. These are abstractions of the threading library provided by the native operating system.

The three main tools are:

This example program shows the use of all three, with two threads interacting: the program's main thread of execution, and a new thread created using ArASyncTask. An ArMutex object is used to keep use of some shared data safe, and then the use of ArCondition is shown.

Threading can be error-prone, since any (perhaps subconcious) assumptions you have about the linear execution of code may not apply to simultaneous threads. Furthermore, different computers will execute multithreaded code in different ways (especially if they have different numbers of CPUs). ARIA's threading tools can help make multiple threads work, and help make multithreaded code portable, but you must always think carefully about how code might execute (including error conditions!) to avoid deadlocks and race conditions.

00001 
00032 #include "Aria.h"
00033 
00034 
00035 /* A subclass of ArASyncTask, to contain a method that runs in a new thread */
00036 class ExampleThread : public ArASyncTask
00037 {
00038   ArCondition myCondition;
00039   ArMutex myMutex;
00040   int myCounter;
00041 public:
00042 
00043   /* Construtor. Initialize counter. */
00044   ExampleThread() : myCounter(0)
00045   {
00046   }
00047 
00048 
00049   /* This method is called in the new thread when launched. The void* parameter
00050    * and return value are platform implementation-specific and can be ignored.
00051    * This method will run in a loop, incrementing the counter each second, but 
00052    * locking the mutex to prevent conflicting access by other threads. 
00053    * If it reaches a value divisible by ten, signal our condition variable.
00054    */
00055   void* runThread(void*) 
00056   {
00057     // Run until the thread is requested to end by another thread.
00058     while(this->getRunningWithLock())
00059     {
00060       myMutex.lock();
00061 
00062       // Increment the counter. 
00063       myCounter++;
00064       ArLog::log(ArLog::Normal, "Example thread: incremented counter to %d.", myCounter);
00065 
00066       // If it's now divisible by 10, signal the condition variable.
00067       if(myCounter % 10 == 0)
00068       {
00069         ArLog::log(ArLog::Normal, "Example thread: Signalling condition.");
00070         myCondition.signal();
00071       }
00072 
00073       // Unlock, then sleep.  We unlock before the sleep, so that while
00074       // we are sleeping, other threads that try to lock the mutex won't
00075       // be blocked until this thread is done sleeping.
00076       myMutex.unlock();
00077       ArUtil::sleep(1000);
00078     }
00079 
00080     ArLog::log(ArLog::Normal, "Example thread: requested stop running, ending thread.");
00081     return NULL;
00082   }
00083 
00084   /* Other threads can call this to wait for a condition eventually
00085    * signalled by this thread.
00086    */
00087   void waitOnCondition()
00088   {
00089     myCondition.wait();
00090   }
00091 
00092   /* Get the counter. Not threadsafe, you must lock the mutex during access. */
00093   int getCounter() { return myCounter; }
00094 
00095   /* Set the countner. Not threadsafe, you must lock the mutex during access. */
00096   void setCounter(int ctr) { myCounter = ctr; }
00097 
00098   /* Lock the mutex object.  */
00099   void lockMutex() { myMutex.lock(); }
00100 
00101   /* Unlock the mutex object. */
00102   void unlockMutex() { myMutex.unlock(); }
00103 
00104 };
00105 
00106 int main()
00107 {
00108   Aria::init();
00109 
00110   ExampleThread exampleThread;
00111 
00112   /* Launch the new thread in the background. This thread (i.e. the main program thread, 
00113    * executing main()) continues immediately after the new thread is created. */
00114   ArLog::log(ArLog::Normal, "Main thread: Running new example thread ...");
00115   exampleThread.runAsync();
00116 
00117   /* Loop, reading the value contained in the ExampleThread object.
00118    * We will also use ArUtil::sleep() to make this thread sleep each iteration,
00119    * instead of running as fast as possible and potentially preventing other
00120    * threads from access to the mutex and the shared counter.
00121    * When the counter reaches 10, break out of the loop and then wait on the
00122    * condition variable.
00123    */
00124   while(true)
00125   {
00126     exampleThread.lockMutex();
00127     int c = exampleThread.getCounter();
00128     exampleThread.unlockMutex(); // we can unlock the mutex now, since we made a copy of the counter.
00129 
00130     printf("Main thread: Counter=%d.\n", c);
00131 
00132     if(c >= 10)
00133       break;
00134 
00135     ArUtil::sleep(600);
00136   }
00137 
00138 
00139   /* This shows how to block on an ArCondition object. 
00140    * wait() will *only* return when the condition object is 
00141    * signaled by the other thread.
00142    */
00143   ArLog::log(ArLog::Normal, "Main thread: Waiting on condition object...");
00144   exampleThread.waitOnCondition();
00145 
00146   ArLog::log(ArLog::Normal, "Main thread: Condition was signaled, and execution continued. Telling the other thread to stop running.");
00147   exampleThread.stopRunning();
00148   
00149   ArLog::log(ArLog::Normal, "Main thread: Waiting for the other thread to exit, then exiting the program.");
00150   do {
00151     ArUtil::sleep(250);
00152   } while(exampleThread.getRunningWithLock());
00153 
00154   ArLog::log(ArLog::Normal, "Main thread: Exiting program.");
00155   return 0;
00156 }

Generated on Fri Jul 31 12:36:37 2009 for Aria by  doxygen 1.4.7