Threads


Wenn man Daten zur Programmlauffzeit aus einer Datei oder gar dem Internet laden will, hat man ein Problem: Das Programm bleibt stehen und läuft erst dann weiter, wenn die angefragten Daten übermittelt worden sind. Das kann mit unter sehr lange dauern und der User muß warten. Das Programm ruckelt und die User Experience ist schlecht.

Als Lösung für dieses Problem gibt es in Java die Möglichkeit zeitintensive Code- Abschnitte in eigenen Programmabschnitten laufen zu lassen, und nur jeden Frame abzufragen, ob die Daten bereits verfügbar sind, oder nicht.

Sind diese verfügbar, werden sie abgerufen und verarbeitet. Sind sie es nicht, läuft das Programm einfach ohne sie weiter.

Dies ist auch in Processing möglich. Es bringt die Klasse (thread) schon mit. Die einfachste Implementierung würde dann wie folgt aussehen.

void setup() { size(200,200);
// Die Funktion eine Funktion soll in einem separaten thread ausgeführt werden
thread("eineFunction");
}

void draw() {

}

void eineFunction() {
// Diese Funktion wird einem eigenen Thread ausgeführt.
}

Damit kann man nun noch nicht soviel anfangen. Daniel Shiffman hat hier dokumentiert, wie man die Thread-Klasse von Processing so erweitern und adaptieren kann, um sie sinnvoll nutzen zu können.

Ich habe die Klasse dann um eine Abfrage mit der YQL (siehe Geodaten und mehr mit der YQL) erweitert:

Wir schreiben also unsere eingene Klasse, nämlich die SimpleThread-Klasse. Um die Funktionen der Thread Klasse von Processing nutzen zu können, erweitern wir diese mit folgender Code-Zeile:

class SimpleThread extends Thread {

Dann daklarieren wir darin unsere Variablen:

boolean running;           // Is the thread running?  Yes or no?
boolean available;         // Daten verfügbar?  Yes or no?
int wait;                  // How many milliseconds should we wait in between executions?
String id;                 // Thread name
int count;                 // counter
float lat, lng;            // Längen und Breitengrad
PApplet parent;            //Wir brauchen eine Instanz von Processing, damit hier seine Funktionen zur Verfügung stehen
XMLElement xmlResponse;

Einige von diesen ebnötigen wir, damit die Thread Klasse funktioniert, andere um unsere Abfrage laufen lassen zu können. Z.B.: das PApplet parent ist eine Instanz der Processing PApplet Klasse. Diese benötigen wir, um Processing-Funktionen in unserer Klasse nutzen zu können.

Dann folgt der Konstruktor, welcher definiert, welche Variablen bei der Erzeugung einer Instanz an die Thread Klasse übergeben werden müssen. Da ist unter anderem wieder unser PApplet dabei.

// Constructor, create the thread
// It is not running by default
SimpleThread (PApplet theApplet, int w, String s) {
wait = w;
running = false;
id = s;
count = 0;
parent = theApplet;
}

Mit dabei können die originalen Processing Thread-Funktionen überschrieben werden, indem wir sie mit eigenen Funktionen gleichen namens ersetzen und sie um unseren eigenen Code ergänzen.

Ein Thread muss gestartet und beendet werden. Dies machen wir mit void start() und void quit(). Bei der Startfunktion ist die Anweisung in der letzten Zeile interessant: super.start(); Sie ruft die Processing Thread Funktion start() auf, damit nach unseren eigenen Anweisungen auch alles aufgerufen wird, was nötig ist um einen Thread erfolgreich zu starten(),

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

Es folgt nun noch der für uns interessanteste Teil des Ganzen, nämlich die Funktion void run(), welche ausgeführt wird, wenn der Thread läuft. Dort kommen nun unsere Abfragen hinein.

// We must implement run, this gets triggered by start()
void run () {
while (running) {
//println(id + ": " + count);
count++;
loc=loc.replace(' ', '_');
loc=loc.replace(",", "%2C");
println ("loc: "+loc);
try {
String restUrl="http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20geo.places%20where%20text%3D%22"+loc+"%22&diagnostics=true";

xmlResponse = new XMLElement(parent, restUrl);
XMLElement[] placeXMLElements = xmlResponse.getChildren("results/place");
// println("Found " + placeXMLElements.length + " places");
if (placeXMLElements.length>0) {
String name = placeXMLElements[0].getChild(2).getContent();
lat = new Float(placeXMLElements[0].getChild(10).getChild(0).getContent());
lng = new Float(placeXMLElements[0].getChild(10).getChild(1).getContent());
}
}
catch (NullPointerException e) {
println("Couldn't launch request: " + e);
};

available=true;

// 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()
}

Zu guter Letzt brauchen wir noch einige Funktionen, um mit unserem Thread kommunizieren und ihn steueren zu können. Alles zusammen sieht nun so aus:


SimpleThread thread1;

String loc;

void setup() {
size(200, 200);

//ein neuer Thread wird erzeugt
thread1 = new SimpleThread(this, 100, "a");
thread1.start();
loc = "vienna";
}

void draw() {
background(255);
fill(0);
println(thread1.available());
// wenn die Daten verfügbar sind, werden sie abgerufen und eine neue Abfrage übergeben
if (thread1.available()) {
println(thread1.getLat()+" " +thread1.getLng());
thread1.setLoc("paris");
}
}

class SimpleThread extends Thread {

boolean running;           // Is the thread running?  Yes or no?
boolean available;         // Daten verfügbar?  Yes or no?
int wait;                  // How many milliseconds should we wait in between executions?
String id;                 // Thread name
int count;                 // counter
float lat, lng;            // Längen und Breitengrad
PApplet parent;            //Wir brauchen eine Instanz von Processing, damit hier seine Funktionen zur Verfügung stehen
XMLElement xmlResponse;

// Constructor, create the thread
// It is not running by default
SimpleThread (PApplet theApplet, int w, String s) {
wait = w;
running = false;
id = s;
count = 0;
parent = theApplet;
}

float getLat() {
return lat;
}

float getLng() {
return lng;
}

int getCount() {
return count;
}

boolean available() {
return available;
}

void setLoc(String newLoc) {
available=false;
loc=newLoc;
}

// 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) {
//println(id + ": " + count);
count++;
loc=loc.replace(' ', '_');
loc=loc.replace(",", "%2C");
println ("loc: "+loc);
try {
String restUrl="http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20geo.places%20where%20text%3D%22"+loc+"%22&diagnostics=true";

xmlResponse = new XMLElement(parent, restUrl);
XMLElement[] placeXMLElements = xmlResponse.getChildren("results/place");
// println("Found " + placeXMLElements.length + " places");
if (placeXMLElements.length>0) {
String name = placeXMLElements[0].getChild(2).getContent();
lat = new Float(placeXMLElements[0].getChild(10).getChild(0).getContent());
lng = new Float(placeXMLElements[0].getChild(10).getChild(1).getContent());
}
}
catch (NullPointerException e) {
println("Couldn't launch request: " + e);
};

available=true;

// 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()
}
// We must implement run, this gets triggered by start()
void run () {
while (running) {
//println(id + ": " + count);
count++;
loc=loc.replace(' ', '_');
loc=loc.replace(",", "%2C");
println ("loc: "+loc);
try {
String restUrl="http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20geo.places%20where%20text%3D%22"+loc+"%22&diagnostics=true";

xmlResponse = new XMLElement(parent, restUrl);
XMLElement[] placeXMLElements = xmlResponse.getChildren("results/place");
// println("Found " + placeXMLElements.length + " places");
if (placeXMLElements.length>0) {
String name = placeXMLElements[0].getChild(2).getContent();
lat = new Float(placeXMLElements[0].getChild(10).getChild(0).getContent());
lng = new Float(placeXMLElements[0].getChild(10).getChild(1).getContent());
}
}
catch (NullPointerException e) {
println("Couldn't launch request: " + e);
};

available=true;

// 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();
}
}
Advertisements

2 Kommentare

  1. Pingback: Processing – Über dieses Weblog « processing – tutorial

  2. Pingback: MySQL Datenbankanbindung mit PHP « processing – tutorial

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s

%d Bloggern gefällt das: