class methods
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!
Documentation from code comments
A threaded base class with a built in mutex for convenience.
Users can extend this base class by public inheritance like this:
class MyThreadedClass: public ofThread
{
public:
...
void threadedFunction()
{
while(isThreadRunning())
{
Threaded function here.
}
}
};
ofThread is a convenient wrapper for Poco::Thread, Poco::Runnable and Poco::Mutex. It represents a simplified (sometimes overly simplified - or simplified in ways that might not make sense for your project) pathway for quickly writing threaded classes. Poco::Runnable represents a class that can be "run" via its void run() method. Poco::Thread is able to spawn a thread and "run" the contents of a class that extends the Poco::Runnable interface (which ofThread does). Poco::FastMutex, (aka ofMutex) is a "mutual exclusion" object that prevents two threads from accessing the same data at the same time. It is important to know that Poco::FastMutex (aka ofMutex) is not "recursive" while Poco::Mutex is. This means that if the same thread attempts to lock a thread while it ALREADY has a lock on the mutex, the program will lock up and go nowhere. Thus, it is important that ofThread subclasses carefully their use of the mutex. Currently ofThread does not lock its own mutex at any point (e.g. ofThread's internal variables are not thread safe). This is a somewhat dangerous convenience that is (theoretically) supposed to make it easier for subclasses to avoid the recursive mutex "problem". The situation that arises from two threads simultanously reading or writing from the same shared data (shared data occupies the same physical location in memory) leads to something called a "race condition", which can lead to deadlocks. A deadlock is as bad as it sounds. It means your program just stops. ofMutex prevents race conditions, deadlocks and crashes by permitting only one thread access to shared data at a time. When using mutexes to protect data, the trick is to always be sure to unlock the mutex when finished. This problem can often be avoided by using an Poco::FastMutex::ScopedLock (aka ofScopedLock). See the the documentation for more information. Finally, there are many cases where it might make more sense to use Poco::Thread, Poco::Runnable and Poco::FastMutex directly rather than using ofThread. Further, cross platform thread management will be alleviated with the std::thread support library included with C++11.
Uncaught Exceptions throw from within ofThread will cause the thread to stop and the Exception will be delivered to the default ofBaseThreadErrorHandler. The ofBaseThreadErrorHandler will print the exception details, if available. The ofBaseThreadErrorHandler offers no opportunity to take corrective action and only allows the user to receive more valuable debugging information about the uncaught exception. Users should design ofThread subclasses to catch and respond to all anticipated exceptions.
getNativeThread()
thread & ofThread::getNativeThread()
Documentation from code comments
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 & ofThread::getNativeThread()
Documentation from code comments
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 ofThread::getThreadId()
Documentation from code comments
Get the unique thread id. \note This is NOT the the same as the operating thread id!
getThreadName()
string ofThread::getThreadName()
Documentation from code comments
Get the unique thread name, in the form of "Thread id#"
Returns: the Thread ID string.
isCurrentThread()
bool ofThread::isCurrentThread()
Documentation from code comments
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 ofThread::isThreadRunning()
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
}
};
Documentation from code comments
Check the running status of the thread.
Returns: true iff the thread is currently running.
lock()
bool ofThread::lock()
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.
Documentation from code comments
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.
sleep(...)
void ofThread::sleep(long milliseconds)
Documentation from code comments
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 ofThread::startThread()
Documentation from code comments
Start the thread. \note Subclasses can directly access the mutex and employ thier own locking strategy.
stopThread()
void ofThread::stopThread()
Documentation from code comments
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, ...).
tryLock()
bool ofThread::tryLock()
Documentation from code comments
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 ofThread::unlock()
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.
Documentation from code comments
Unlock the mutex.
This will only unlocks the mutex if it was previously by the same calling thread.
waitForThread(...)
void ofThread::waitForThread(bool callStopThread=true, long milliseconds)
Documentation from code comments
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 ofThread::yield()
Documentation from code comments
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.
Last updated 星期二, 19 十一月 2024 17:25:39 UTC - 2537ee49f6d46d5fe98e408849448314fd1f180e
If you have any doubt about the usage of this module you can ask in the forum.
If you want to contribute better documentation or start documenting this section you can do so here
If you find anything wrong with this docs you can report any error by opening an issue