daniel shiffman

Threads

If you are looking for a tutorial about using threads in Processing, I’ve adapted this page on the Processing wiki:

http://wiki.processing.org/w/Threading

Examples:

Related:

Exercises (optional):

Threading

We’re quite familiar with the idea of writing a program that follows a specific sequence of steps as outlined in, say, a main() function. A Thread is also a series of steps with a beginning, a middle, and an end. A thread’s sequence, however, can run independently of the main program. In fact, we can launch any number of threads at one time and they will all run concurrently. Visit the Java site for a more involved explanation.

This is incredibly useful when it comes to data mining, as we can have separate threads retrieving different pieces of information from the network. If one 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, we simply extend the Thread class.

public 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:

    private boolean running;           // Is the thread running?  Yes or no?
    private int wait;                  // How many milliseconds should we wait in between executions?
    private String id;                 // Thread name
    private int count;                 // counter

    // Constructor, create the thread
    // It is not running by default
    public 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:

    // Overriding "start()"
    public void start ()
    {
        // Set running equal to true
        running = true;
        // Print messages
        System.out.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()
    public void run ()
    {
        while (running &#038;&#038; count < 10){
            System.out.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
    public void quit()
    {
        System.out.println("Quitting.");
        running = false;  // Setting running to false ends the loop in run()
        interrupt(); // in case the thread is waiting. . .
    }
}

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();

Here’s a more sophisticated example that involves reading the Google news RSS feed in a thread.

Synchronized Threads

Writing an independent thread is easy, nevertheless, there are often times where one needs to access and manipulate information inside a thread externally (perhaps in the “main” program, or another thread, etc.) This problem occurs in data visualization programs, where we might require that an animation driven by data from the network runs smoothly, without having to pause and wait each time that data reloads. Let’s look at this applet:

newsapplet

Non threaded version

Threaded version

A few things have changed. For example, we have added a boolean variable “available” to indicate to the main program when the thread has completed loading a new set of headlines.

  private boolean available;         // Is new news available?

At the end of the “check()” method, which reads the news feed, available is set to true:

  private synchronized void check() {
    headlines = new ArrayList();
    // Create a URL object and open an InputStream
    A2ZXmlReader xmlreader = null;
    try {
      xmlreader = new A2ZXmlReader("http://itp.nyu.edu/icm/proxy/proxy.php?url=http://news.google.com/?output=rss");
      // Call our recursive search function to locate the element we want

      ArrayList elements = new ArrayList();
      xmlreader.fillArrayList(xmlreader.getRoot(),"title",elements);
      for (int i = 0; i < elements.size(); i++) {
        Element e = (Element) elements.get(i);
        // As long as we find the element
        if (e != null) {
          Node n = e.getFirstChild();
          String headline = n.getNodeValue();
          headline = headline.replaceAll("&#39;","'");
          if ((!headline.matches("Google News")) &#038;&#038; (headlines.size() < maxheadlines))  {
            headlines.add(headline);
          }
        }
      }
      available = true;
      notifyAll();  // let's notify everyone that the headlines have been updated
    }
    catch (Exception e) {
      System.out.println("Something went wrong. " + e);
    }
  }

Note also the use of synchronized keyword. This indicates that the thread should be locked down while this method is executed, i.e. other threads cannot have access to its internal data. We're also using notifyAll(), which alerts any threads that are waiting for data that it is ready. Now, whenever the updated ArrayList of headlines is retrieved by an external source, available is reset to false while we wait for the next reload time:

  public synchronized ArrayList getHeadlines() {
    // We should put a while (!available) loop here
    // but since we are explicitly only calling this function if available is true, we're ok
    available = false;
    notifyAll(); // let's notify everyone that available has changed
    return headlines;
  }

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

  public boolean available() {
    return available;
  }

Our main program checks if new information is 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() {
  // If there is new news available, get it!
  if (news.available()) {
    headlines = news.getHeadlines();
  }
}

Making your own Processing library

Looking closely at the above applets, you’ll notice the following line of code:

import a2z.*;                  // Lookie, our code is a Processing library!

Instead of having to include all of our java classes in a “code” folder for each sketch, or create each one as a separate .java tab, we can package up these classes into a JAR file and include it as a Processing library. Here are the steps:

  • Create a set of classes as a java package — say it’s called: mylib.jar
  • Export the package as a jar. This can be accomplished in Eclipse via “Export” or command line like so: “jar cvf mylib.jar Foo.class Bar.class”
  • Take the jar file and place it in: /Applications/Processing 0109/libraries/mylib/library/
  • Import the library, i.e.: “import mylib.*;”
  •  
    It’s also possible to write a library that registers itself with a parent applet and knows when certain events occur. We can also provide a Method instance (from: java.lang.reflect) that can be invoked in the parent applet when a given event occurs.

    To implement these features, you’ll want your class to contain a PApplet reference:

    package mylib;
    
    import processing.core.*;
    
    public class MyLib {
      PApplet parent;
    
      public MyLib(PApplet p) {
        parent = p;
        parent.registerDraw(this);
        parent.registerDispose(this);
      }
    
      public void draw() {
         // Code here will be executed at the end of draw() in the parent applet
      }
    
      public void dispose() {
        // Code in here will be executed when the parent applet shuts down
        // (note: http://dev.processing.org/bugs/show_bug.cgi?id=183)
      }
    
    }
    

    Other methods you can register are: keyEvent, mouseEvent, pre, endFrame, stop, post. Full documentation is available in howto.txt (in the Processing libraries folder), and you may also find the forums helpful.

    To create a callback method that the user writes in the main applet, create a Method instance in your library class:

    Method eventMethod;
    

    Then, in the constructor, you can check and see if that method exists in the parent:

        try {
          // Looking for a method called "myEvent", with one argument of PEvent type
          eventMethod = parent.getClass().getMethod("myEvent", new Class[] { PEvent.class });
        }
        catch (Exception e) {
          System.out.println("Method not in parent class? " + e);
        }
    

    Later, whenever you feel like it, you can invoke that method:

    // As long as the method exists
    if (eventMethod != null) {
      try {
        // Call the method with this object as the argument!
        eventMethod.invoke(parent, new Object[] {this} );
      } catch (Exception e) {
        // Error handling
        System.err.println("I couldn't invoke that method for some reason.");
        e.printStackTrace();
        eventMethod = null;
      }
    }
    

    Your processing applet would then include the method automatically called. Note this is exactly how serialEvent(), captureEvent(), etc. work!

    import pevent.*;
    
    PEvent event;
    
    void setup() {
      event = new PEvent(this);
    }
    
    void myEvent(PEvent e) {
      println(e.read());
    }
    
    
    void draw() {
    }
    

    Full source:

    comments powered by Disqus