Threads in Processing.org





Threads in Processing are very simple and easy to use.  So simple in fact that there are a few things to make note of.  Threads are useful if you want to do some background work, or if you want to have something like a loading screen to display whilst you initially sort out some data.  Using threads help you to do some computation outside from the draw() loop that otherwise dominates the processing sketch.  Take a look at the quick sketch below:



volatile boolean THREAD_DONE;

void setup() {

   // Make sure we set out flag
   // to false before calling thread.
   THREAD_DONE = false;

   // Start the thread (easy)
   thread( "myThread" );
}


void draw() {

   if( !THREAD_DONE ) {
       // Draw an animation
   } else {
       // Normal operation
   }

}

void myThread() {

   // Do some work here

   // And set the flag when finished.
   THREAD_DONE = true;

   // thread will not exist after return.
   return;
}
First, note the word volatile before the type boolean for our flag THREAD_DONE.  This is a keyword, and it tells the computer that THREAD_DONE might unexpectedly change its value.  If we were reading THREAD_DONE within a loop within draw(), the compiler might try to optimise your code by keeping track of when it was last written to in order to save time.  It would assume it was the last value as before, within the draw() loop.  This can fall short if you are changing a variable from different threads.

We have to use the boolean flag THREAD_DONE at the top of our program (global scope) because we can't pass any arguments to our thread - this is a limitation to the vanilla blend of processing.  Notice that to start a thread, we simply use the function thread() and supply the name of another function as a string, one we define ourselves.  Processing looks for that function by itself.  So above it was thread( "myThread" ).  That means the function declaration that will run as our thread needs to look like void myThread( ) - empty brackets, no variable arguments.

Using a global variable as a flag between threads is risky.  So as a general rule, make sure that you only write global the variable in setup(), write the variable from within the generated thread, and read from the variable from the main draw() loop.  You'll have to break this rule if you need to call your thread more than once.  It could also be something other than a boolean.  Your thread could update an int, float, double ... just remember to put put the volatile keyword first and be methodical.

There is a safer way to both read and write variables across multiple threads within processing.  You might need to do this if you start a thread that will always run in the background until your sketch ends.  Such a thread might constantly update information in the background, which you use within draw().  Take a look at the example below:

// A very simple class to contain
// data relevant to the thread.
class  threadData_c {
   int some_data;
   
   threadData_c() {
     some_data = -1;
   }
}

// Our global declaration of threadData_c as
// a variable called thread_data
synchronized threadData_c thread_data;



void setup() {

   // Initialise our threadData_c variable.
   thread_data = new threadData_c();
 
   // Start the thread (easy)
   thread( "myThread" );
}


void draw() {

   println( "Thread data: " + thread_data.some_data );
  
}

void myThread() {

   // This thread will live forever.  
   // It would be best to implement a way to exit.
   while( true ) {

     // just increment for the sake of it.
     thread_data.some_data += 1;
   
   }
   
}

That keyword synchronized ensures that only one part of your whole program can use the variable at any time.  It becomes locked.  As far as I know, it can only be use on objects (not primitives, like int, float, double... etc), which is why we define that basic class at the top of the sketch.  You could populate the class with all sorts of data types, to both communicate with your thread, and from the thread back to draw().

So you might want to play with the above code to see how you can break it, when it fails.

Hope that helps!