Threading

From Processing

Jump to: navigation, search

This tutorial is based on my (somewhat outdated) Java tutorial about threads:

The source code below from the examples below can be downloaded from here:

Other Resources:




You are likely familiar with the idea of writing a program that follows a specific sequence of steps -- setup() first then draw() over and over and over again! A Thread is also a series of steps with a beginning, a middle, and an end. A Processing sketch is a single thread, often referred to as the "Animation" thread. Other threads sequences, however, can run independently of the main "Processing" sketch. In fact, you can launch any number of threads at one time and they will all run concurrently.

Processing does this all the time, whenever you write an event callback, such as serialEvent(), or captureEvent(), etc. these functions are triggered by a different thread running behind the scenes, and they alert Processing whenever they have something to report. This is useful whenever you need to perform a task that takes too long and would slow down the main animation's frame rate, such as grabbing data from the network (XML, database, etc.) If a separate thread gets stuck or has an error, the entire program won't grind to a halt, since the error only stops that individual thread. To create independent, asynchronous threads, you can use the thread() function built into Processing.


void setup() {
  size(200,200);
  thread("someFunction");
}
 
void draw() {
 
}
 
void someFunction() {
  // This function will run as a thread when called via
  // thread("someFunction") as it was in setup!
}

The thread() function receives a String as an argument. The String should match the name of the function you want to run as a thread. An example that does this can be found under Topics --> Advanced Data in the Processing examples.

While using the thread() function is a very simple way of getting an independent thread, it is somewhat limited. Being able to make a thread object is a great deal more powerful, and this can be done by extending the Thread class.

class SimpleThread extends Thread {

It's useful to include a few fields describing the thread's properties. In this example, we'll use a boolean variable to indicate whether the thread is running or not, a integer to count how many times the thread has executed its loop, a String to give the thread an ID, and an integer to hold the number of milliseconds in between each execution. The constructor will initialize these values:

  boolean running;           // Is the thread running?  Yes or no?
  int wait;                  // How many milliseconds should we wait in between executions?
  String id;                 // Thread name
  int count;                 // counter
 
  // Constructor, create the thread
  // It is not running by default
  SimpleThread (int w, String s) {
    wait = w;
    running = false;
    id = s;
    count = 0;
  }

We are then going to override two functions from the parent Thread class:

  • start()< -- Start causes the thread to begin its execution. The run() method is called automatically for you, as long as you make sure to include super.start(). We aren't required to override this method, but it's useful since we may want our own custom code to execute when the thread begins.
  • run() -- Nothing happens in the parent version of this function. It's up to us to write code for whatever we want the thread to do when it runs. When the end of this function is reached, the thread ends. To keep it running over a period of time, a while loop (testing the running variable) is used.
  // Overriding "start()"
  void start () {
    // Set running equal to true
    running = true;
    // Print messages
    println("Starting thread (will execute every " + wait + " milliseconds.)"); 
    // Do whatever start does in Thread, don't forget this!
    super.start();
  }
 
 
   // We must implement run, this gets triggered by start()
  void run () {
    while (running && count < 10) {
      println(id + ": " + count);
      count++;
      // Ok, let's wait for however long we should wait
      try {
        sleep((long)(wait));
      } catch (Exception e) {
      }
    }
    System.out.println(id + " thread is done!");  // The thread is done when we get to the end of run()
  }

Finally, it's useful to create a quit() method, in case we want to interrupt the thread and stop it. (Note that stop() is now deprecated).

  // Our method that quits the thread
  void quit() {
    System.out.println("Quitting."); 
    running = false;  // Setting running to false ends the loop in run()
    // IUn case the thread is waiting. . .
    interrupt();
  }

Most likely, if you are using a Thread, you are going to want to query the thread for some information from your "main" program (i.e. setup/draw). It's a bit silly in this basic example because all the thread is doing is counting, but we can still see the idea of this in action by writing a function to return the current "count" value.

  int getCount() {
    return count;
  }

Once we've completed our Thread class, creating and running threads is easy!

SimpleThread thread1 = new SimpleThread(1000,"cat");
SimpleThread thread2 = new SimpleThread(1500,"dog");
thread1.start();
thread2.start();

Let's take a look at everything together.

SimpleThread thread1;
SimpleThread thread2;
 
void setup() {
  size(200,200);
  thread1 = new SimpleThread(1000,"a");
  thread1.start();
 
  thread2 = new SimpleThread(1200,"b");
  thread2.start();
 
}
 
void draw() {
  background(255);
  fill(0);
 
  int a = thread1.getCount();
  text(a,10,50);
 
  int b = thread2.getCount();
  text(b,10,150);
}
 
class SimpleThread extends Thread {
 
  boolean running;           // Is the thread running?  Yes or no?
  int wait;                  // How many milliseconds should we wait in between executions?
  String id;                 // Thread name
  int count;                 // counter
 
  // Constructor, create the thread
  // It is not running by default
  SimpleThread (int w, String s) {
    wait = w;
    running = false;
    id = s;
    count = 0;
  }
 
  int getCount() {
    return count;
  }
 
  // Overriding "start()"
  void start () {
    // Set running equal to true
    running = true;
    // Print messages
    println("Starting thread (will execute every " + wait + " milliseconds.)"); 
    // Do whatever start does in Thread, don't forget this!
    super.start();
  }
 
 
  // We must implement run, this gets triggered by start()
  void run () {
    while (running && count < 10) {
      println(id + ": " + count);
      count++;
      // Ok, let's wait for however long we should wait
      try {
        sleep((long)(wait));
      } catch (Exception e) {
      }
    }
    System.out.println(id + " thread is done!");  // The thread is done when we get to the end of run()
  }
 
 
  // Our method that quits the thread
  void quit() {
    System.out.println("Quitting."); 
    running = false;  // Setting running to false ends the loop in run()
    // IUn case the thread is waiting. . .
    interrupt();
  }
}

Threads and external data

Let's look at a more practical example, say, a visualization that continuously reloads the data source (say, every 30 seconds). I've got a sample PHP script that loads the 10 most recent tweets with '#Processing': [1]. Using loadStrings(), we can grab these into Processing and show them onscreen, i.e.:

// An array of strings
String[] tweets;
 
float offset = 0;
 
void setup() {
  size(600,400);
  // Filling the array from our PHP script
  tweets = loadStrings("http://www.learningprocessing.com/php/twitter/searchtweets.php?query=%23Processing");
}
 
void draw() {
  if (frameCount % 30 == 0) {
    tweets = loadStrings("http://www.learningprocessing.com/php/twitter/searchtweets.php?query=%23Processing");
  }
 
  background(255);
  // Drawing all the strings
  for (int i = 0; i < tweets.length; i++) {
    fill(0);
    text(tweets[i],10,(20+i*20 + offset) % height);
  }
 
  offset++;
}

If you run this, you'll notice that it pauses every 30 frames while loading the data. To avoid this, we'll want to farm this work out to a separate thread. We need to add a few things to our original SimpleThread class to deal with this new situation.

First, we'll include a boolean variable "available" to indicate to the main program when the thread has completed loading a new set of headlines.

 boolean available;

And after we call loadStrings(), from within the run() method, we can set available equal to true.

  void run () {
    while (running) {
      tweets = loadStrings("http://www.learningprocessing.com/php/twitter/searchtweets.php?query=%23Processing");
      // New data is available!
      available = true;
      try {
        // Wait five seconds
        sleep((long)(5000));
      } 
      catch (Exception e) {
      }
    }
  }

Finally, we include a method available() to return true or false based on whether or not new information has arrived:

  boolean available() {
    return available;
  }

Our main program checks if new tweets are available, and acts accordingly. It never has to pause and wait for the data to be loaded since all that work is taken care of in the thread. We check every time through Processing's draw() loop.

void draw() {
// Check to see if there is new data available from the thread
  if (tweetThread.available()) {
    tweets = tweetThread.getTweets();
  }
}
Personal tools