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 }
1.4.7