ofThread
ofThread is a thread base class with a built in mutex. A thread is essentially a mini processing object you can run in parallel to your main application loop and is useful for running time intensive operations without slowing down your app.
####Implementing a Thread
For instance, you have to grab from a camera and waiting for an image slows down your app. You can offload this wait to a thread which tells the main app when an image is ready. To do this, you create a class that inherits from the ofThread class and implement the threadedFunction() function.
Declaration in a .h file:
class MyThread : public ofThread {
...
// the thread function
void MyThread::threadedFunction() {
// start
while(isThreadRunning()) {
cam.update();
if(cam.isFrameNew()) {
// load the image
image.setFromPixels(cam.getPixels());
}
}
// done
}
ofVideoGrabber cam; // the cam
ofImage image;
};
In the application .h inside the ofApp class declare an object of type MyThread like:
// create object
MyThread thread;
then in the .cpp file:
void ofApp::setup() {
// start the thread
thread.startThread(true); // blocking
}
void ofApp::update() {
// do something with the thread is running
}
void ofApp::exit() {
// stop the thread
thread.stopThread();
}
####Shared Resources
With this great power, however, comes great responsibility. If both the thread and your main app loop try to access the image at the same time, bad things happen inside your computer and the app will crash. The image is a considered a "shared resource" and you need to make sure to lock access to it so that only 1 thread can access it a time. You can do this using a "mutal exclusion" object by called lock() when you want to access the resource, then unlock() when you are done.
Declaration in a .h file:
class MyThread : public ofThread {
...
// the thread function
void MyThread::threadedFunction() {
// start
while(isThreadRunning()) {
cam.update();
if(cam.isFrameNew()) {
// lock access to the resource
lock();
// load the image
image.setFromPixels(cam.getPixels());
// done with the resource
unlock();
}
}
// done
}
ofVideoGrabber cam; // the cam
ofImage image; // the shared resource
};
In the .cpp file:
void ofApp::setup() {
// start the thread
thread.startThread(true, false); // blocking, non verbose
}
void ofApp::update() {
// lock access to the resource
thread.lock();
// copy image
myImage = thread.image;
// done with the resource
thread.unlock();
}
void ofApp::exit() {
// stop the thread
thread.stopThread();
}
####Exiting Nicely
As a thread is running in parallel with your application main loop, it's important to remember to tell it to stop before exiting the app. If you don't, you'll get weird errors or hangs because you aren't being nice to your threads. Depending on how you started your thread (blocking or non-blocking mode), you will either stop it for wait for it to finish. See the stopThread() & waitForThread() functions.
####Debugging
Thread errors are notoriously difficult to debug sometimes. You will probably see a "Bad Access" runtime error or something similar if multiple threads are trying to access a shared resource simultaneously. Other times, nothing will happen as the thread may be stuck in an infinite loop and you can't stop it. Wee! We assume if you've read this far, you probably accept the difficulties in order to reap the thread speed rewards.
A useful tool in debugging thread timing and access is the ofThread verbose mode which prints thread events such as starting, stopping, and mutex locking/unlocking. Simply set verbose=true when calling startThread(). Another trick is to use an ofSleepMillis() inside the thread to slow it down so you can see the timing better while debugging it.
####HOO RAH
Ok soldier, lock and load ... good luck!
getNativeThread( )
thread & getNativeThread()Get a reference to the underlying Poco thread.
Poco::Thread provides a clean cross-platform wrapper for threads. On occasion, it may be useful to interact with the underlying Poco::Thread directly.
Returns: A reference to the backing Poco thread.
getNativeThread( )
const thread & getNativeThread()Get a const reference to the underlying Poco thread.
Poco::Thread provides a clean cross-platform wrapper for threads. On occasion, it may be useful to interact with the underlying Poco::Thread directly.
Returns: A reference to the backing Poco thread.
getThreadId( )
thread::id getThreadId()Get the unique thread id. \note This is NOT the the same as the operating thread id!
getThreadName( )
string getThreadName()Get the unique thread name, in the form of "Thread id#"
Returns: the Thread ID string.
isCurrentThread( )
bool isCurrentThread()Query whether the current thread is active.
In multithreaded situations, it can be useful to know which thread is currently running some code in order to make sure only certain threads can do certain things. For example, OpenGL can only run in the main execution thread. Thus, situations where a thread is responsible for interacting with graphics resources may need to prevent graphics updates unless the main thread is accessing or updating resources shared with this ofThread (or its subclass).
if(myThread.isCurrentThread())
{
// do some myThread things,
// but keep your hands off my resources!
}
else if(ofThread::isMainThread())
{
// pheew! ok, update those graphics resources
}
By way of another example, a subclass of ofThread may have an update() method that is called from ofBaseApp during the execution of the main application thread. In these cases, the ofThread subclass might want to ask itself whether it can, for instance, call update() on an ofImage, in order to send copy some ofPixels to an ofTexture on the graphics card.
Returns: True iff this ofThread the currently active thread.
isThreadRunning( )
bool isThreadRunning()Check the running status of the thread.
Returns: true iff the thread is currently running.
Returns true if the thread is currently running. This is especially useful inside the thread's threadedFunction() when you want it to loop continuously until it's told to exit:
class MyThread : public ofThread {
...
// the thread function
void MyThread::threadedFunction() {
// start
while(isThreadRunning()) {
// do stuff
}
// done
}
};
lock( )
bool lock()Lock the mutex.
If the thread was started startThread(true), then this call will wait until the mutex is available and return true. If the thread was started startThread(false), this call will return true iff the mutex is was successfully acquired.
Returns: true if the lock was successfully acquired.
Try to lock the mutex.
If the thread was started in blocking mode in startThread(), any thread (including your app main loop) will wait until the mutex is unlocked.
If the thread is non-blocking, this call will immediately return a true or false if the mutex is available. It is up to you to make sure the resource is not being used when accessing it. See the Wikipedia article on Non-blocking for reasons as to why using a non-blocking thread might be more advantageous.
ofThread( )
ofThread()Create an ofThread.
run( )
void run()setThreadName( ... )
void setThreadName(const string &name)sleep( ... )
void sleep(long milliseconds)Tell the thread to sleep for a certain amount of milliseconds.
This is useful inside the threadedFunction() when a thread is waiting for input to process:
void MyThreadedClass::threadedFunction()
{
// start
while(isThreadRunning())
{
// bReadyToProcess can be set from outside the threadedFuntion.
// perhaps by another thread that downloads data, or loads
// some media, etc.
if(bReadyToProcess == true)
{
// do some time intensive processing
bReadyToProcess = false;
}
else
{
// sleep the thread to give up some cpu
sleep(20);
}
}
// done
}
If the user does not give the thread a chance to sleep, the thread may take 100% of the CPU core while it's looping as it waits for something to do. This may lead to poor application performance.
Parameters:
milliseconds The number of milliseconds to sleep.
startThread( )
void startThread()Start the thread. \note Subclasses can directly access the mutex and employ thier own locking strategy.
stopThread( )
void stopThread()Stop the thread.
This does immediately stop the thread from processing, but will only set a flag that must be checked from within your threadedFunction() by calling isThreadRunning(). If the user wants to both stop the thread AND wait for the thread to finish processing, the user should call waitForThread(true, ...).
threadedFunction( )
void threadedFunction()The thread's run function.
Users must overide this in your their derived class and then implement their threaded activity inside the loop. If the the users's threadedFunction does not have a loop, the contents of the threadedFunction will be executed once and the thread will then exit.
For tasks that must be repeated, the user can use a while loop that will run repeatedly until the thread's threadRunning is set to false via the stopThread() method.
void MyThreadedClass::threadedFunction()
{
// Start the loop and continue until
// isThreadRunning() returns false.
while(isThreadRunning())
{
// Do activity repeatedly here:
// int j = 1 + 1;
// This while loop will run as fast as it possibly
// can, consuming as much processor speed as it can.
// To help the processor stay cool, users are
// encouraged to let the while loop sleep via the
// sleep() method, or call the yield() method to let
// other threads have a turn. See the sleep() and
// yield() methods for more information.
// sleep(100);
}
}
This is the thread run function, the heart of your thread.
You need to override this in your derived class and implement your thread stuff inside. If you do not have a loop inside this function, it will run once then exit. If you want the thread to run until you signal it to stop, use a while loop inside that checks if the thread is should keep running.
Declaration in a .h file:
class MyThread : public ofThread {
...
// the thread function
void MyThread::threadedFunction() {
// start
while(isThreadRunning()) {
// do stuff
}
// done
}
};
In the .cpp file:
void ofApp::setup() {
// create object
MyThread thread;
// start the thread
thread.startThread(true, false); // blocking, non verbose
}
void ofApp::update() {
// do something else while the thread is running
}
void ofApp::exit() {
// stop the thread
thread.stopThread();
}
tryLock( )
bool tryLock()Tries to lock the mutex.
If the thread was started startThread(true), then this call will wait until the mutex is available and return true. If the thread was started startThread(false), this call will return true iff the mutex is was successfully acquired.
Returns: true if the lock was successfully acquired.
unlock( )
void unlock()Unlock the mutex.
This will only unlocks the mutex if it was previously by the same calling thread.
Unlock the mutex.
This only unlocks the mutex if the calling thread had previously locked it, otherwise the functions does nothing and does not block.
waitForThread( ... )
void waitForThread(bool callStopThread=true, long milliseconds)Wait for the thread to exit (aka "joining" the thread).
This method waits for a thread will "block" and wait for the thread (aka "join" the thread) before it returns. This allows the user to be sure that the thread is properly cleaned up. An example of when this might be particularly important is if the threadedFunction() is opening a set of network sockets, or downloading data from the web. Destroying an ofThread subclass without releasing those sockets (or other resources), may result in segmentation faults, error signals or other undefined behaviors.
Parameters:
callStopThread Set stop to true if you want to signal the thread to exit before waiting. This is the equivalent to calling stopThread(). If you your threadedFunction uses a while-loop that depends on isThreadRunning() and you do not call stopThread() or set stop == true, waitForThread will hang indefinitely. Set stop == false ONLY if you have already called stopThread() and you simply need to be sure your thread has finished its tasks.
milliseconds If millseconds is set to INFINITE_JOIN_TIMEOUT, the waitForThread will wait indefinitely for the thread to complete. If milliseconds is set to a lower number (e.g. 10000 for 10 seconds), waitForThread will wait for 10000 milliseconds and if the thread has not yet stopped it will return and log an error message. Users are encouraged to use the default INFINITE_JOIN_TIMEOUT. If the user is unhappy with the amount of time it takes to join a thread, the user is encouraged to seek more expedient ways of signalling their desire for a thread to complete via other signalling methods such as Poco::Event, Poco::Condition, or Poco::Semaphore.
See also: http://pocoproject.org/slides/090-NotificationsEvents.pdf
See also: http://pocoproject.org/docs/Poco.Condition.html
See also: http://pocoproject.org/docs/Poco.Event.html
See also: http://pocoproject.org/docs/Poco.Semaphore.html
yield( )
void yield()Tell the thread to give up its CPU time other threads.
This method is similar to sleep() and can often be used in the same way. The main difference is that 1 millisecond (the minimum sleep time available with sleep()) is a very long time on modern processors and yield() simply gives up processing time to the next thread, instead of waiting for number of milliseconds. In some cases, this behavior will be preferred.