Eine der wesentlichen Herausforderungen eines komplexen Systems besteht darin, bei der Ausführung von Tasks reaktionsfähig zu bleiben. In einem erweiterbaren System ist diese Herausforderung um so größer, da Komponenten, die nicht für die gemeinsame Verwendung entworfen wurden, auf die gleichen Ressourcen zugreifen. Das Paket org.eclipse.core.runtime.jobs geht auf diese Herausforderung ein und bietet eine Infrastruktur für die Zeitplanung, Ausführung und Verwaltung gleichzeitig laufender Verarbeitungsschritte. Diese Infrastruktur basiert auf der Verwendung von Jobs als Verarbeitungseinheiten, die asynchron ausgeführt werden können.
class TrivialJob extends Job { public TrivialJob() { super("Trivial Job"); } public IStatus run(IProgressMonitor monitor) { System.out.println("This is a job"); return Status.OK_STATUS; } }Im folgenden Ausschnitt wird der Job erstellt und terminiert:
TrivialJob job = new TrivialJob(); System.out.println("About to schedule a job"); job.schedule(); System.out.println("Finished scheduling a job");Die Ausgabe dieses Programms hängt von der Ablaufsteuerung ab. Dies bedeutet, dass es unmöglich ist, sicherzustellen, wann die Methode run des Jobs im Verhältnis zum Thread, das den Job erstellt und terminiert hat, ausgeführt wird. Für die Ausgabe stehen zwei Optionen zur Verfügung:
About to schedule a job This is a job Finished scheduling a joboder:
About to schedule a job Finished scheduling a job This is a job
Um sicherzustellen, dass ein Job beendet ist bevor Sie fortfahren, können Sie die Methode join() verwenden. Diese Methode blockiert das aufrufende Modul, bis der Job beendet oder der aufrufende Thread unterbrochen ist. Im folgenden Ausschnitt ist das obige Beispiel auf etwas deterministischere Art abgeändert worden:
TrivialJob job = new TrivialJob(); System.out.println("About to schedule a job"); job.schedule(); job.join(); if (job.getResult().isOk()) System.out.println("Job completed with success"); else System.out.println("Job did not complete successfully");Angenommen, der Aufruf join() wird nicht unterbrochen, gibt diese Methode garantiert das folgende Ergebnis aus:
About to schedule a job This is a job Job completed with success
Natürlich ist es normalerweise nicht sonderlich nützlich, einen Job direkt nach der Terminierung zu verknüpfen, da hierdurch kein gemeinsamer Zugriff erzielt wird. In diesem Fall könnten Sie die Verarbeitungsschritte der Methode 'run' des Jobs auch genauso gut direkt im aufrufenden Thread durchführen. An späterer Stelle folgen noch weitere Beispiele, bei denen die Verwendung von 'join' sinnvoller ist.
Der letzte Ausschnitt verwendet außerdem das Ergebnis (result) des Jobs. Das Ergebnis ist das Objekt IStatus , das von der Methode run() des Jobs zurückgegeben wird. Sie können dieses Ergebnis verwenden, um beliebige erforderliche Objekte von der Methode 'run' des Jobs zurückzugeben. Das Ergebnis kann auch für die Anzeige eines Fehlers (indem es einen IStatus mit der Bewertung IStatus.ERROR zurückgibt) oder Abbruchs (IStatus.CANCEL) verwendet werden.
Es wurde bereits dargestellt, wie ein Job terminiert werden und seine Beendigung abgewartet werden kann. Jobs sind aber noch deutlich vielseitiger einsetzbar. Wenn Sie einen Job terminieren, dann aber entscheiden, dass er nicht mehr benötigt wird, so kann er über die Methode cancel() gestoppt werden. Wenn der Job bei Abbruch noch nicht läuft, so wird er sofort gelöscht und wird nicht ausgeführt. Wenn er allerdings bereits läuft, so liegt es im Ermessen des Jobs, auf den Abbruch zu reagieren oder nicht. Beim Abbruch eines Jobs ist es oft vorteilhaft, abzuwarten, bis er die Methode join() verwendet. Der folgende Code zeigt ein häufig verwendetes Beispiel für die Vorgangsweise beim Abbruch eines Jobs. Er wartet auch, bis der Job fertig ist, bevor er fortfährt:
if (!job.cancel()) job.join();
Wenn der Abbruch nicht sofort umgesetzt wird, gibt cancel() den Wert 'false' zurück, und das aufrufende Programm verwendet join(), um abzuwarten, bis der Job erfolgreich abgebrochen wurde.
Die Methode sleep() ist etwas weniger drastisch. Wenn der Job noch nicht läuft, setzt ihn die Methode wiederum bis auf weiteres in eine Warteschleife. Der Job bleibt allerdings in der Plattform erhalten und kann durch einen Aufruf wakeUp() wieder zur Warteschlange hinzugefügt und schließlich ausgeführt werden.
Ein Job durchläuft mehrere Statuswerte. Er kann nicht nur durch API wie cancel() oder sleep() bearbeitet werden, sein Status ändert sich auch, während die Plattform den Job ausführt und fertigstellt. Jobs können die folgenden Statuswerte annehmen:
Ein Job kann nur auf inaktiv gesetzt werden, wenn er zur Zeit den Status WAITING hat. Durch eine Aktivierung nimmt ein inaktiver Job wieder den Status WAITING an. Bei Abbruch geht wird der Status eines Jobs auf NONE zurückgesetzt.
Wenn Ihr Plug-in Informationen über den Status eines bestimmten Jobs benötigt, so kann es eine Listener-Funktion für Jobänderungen registrieren, die benachrichtigt wird, wenn ein Job zu einem neuen Status übergeht. Dies ist für eine Fortschrittsanzeige oder andere Berichte über einen Job nützlich.
Die Methode addJobChangeListener für Jobs kann verwendet werden, um für einen bestimmten Job eine Listener-Funktion zu registrieren. IJobChangeListener definiert ein Protokoll, um auf Statusänderungen eines Jobs zu reagieren:
In all diesen Fällen erhält die Listener-Funktion ein IJobChangeEvent , das den Job, dessen Status sich ändert, sowie den neuen Status (falls eine Änderung durchgeführt wurde) angibt.
Hinweis: Jobs definieren auch die Methode getState(), über die der (relativ) aktuelle Status eines Jobs abgerufen werden kann. Dieses Ergebnis ist allerdings nicht immer zuverlässig, da Jobs in einem anderen Thread ausgeführt werden und zu dem Zeitpunkt, an dem der Aufruf eine Rückmeldung liefert, bereits wieder den Status geändert haben können. Um Statusänderungen eines Jobs festzustellen werden Listener-Funktionen empfohlen.
IJobManager definiert ein Protokoll für die Verwendung aller Jobs im System. Plug-ins, die den Fortschritt anzeigen oder auf eine andere Art mit der Job-Infrastruktur arbeiten, können denIJobManager verwenden, um Tasks (z.B. Aussetzen aller Jobs im System, Identifizierung laufender Jobs oder Erhalt von Fortschrittsrückmeldungen über einen bestimmten Job) auszuführen. Auf den Job-Manager der Plattform kann über die API Plattform zugegriffen werden:
IJobManager jobMan = Platform.getJobManager();
Plug-ins, die Informationen über die Statuswerte aller Jobs im System benötigen, können eine Listener-Funktion für Jobänderungen für den Job-Manager registrieren, anstatt für viele einzelne Jobs Listener-Funktionen zu registrieren.
Manchmal ist es für ein Plug-in leichter, eine Gruppe zusammengehöriger Jobs als einzelne Einheit zu bearbeiten. Dies kann über den Einsatz von Jobfamilien erreicht werden. Ein Job deklariert, dass er zu einer bestimmten Familie gehört, indem er die Methode belongsTo überschreibt:
public static final String MY_FAMILY = "myJobFamily"; ... class FamilyJob extends Job { ... public boolean belongsTo(Object family) { return family == MY_FAMILY; } }Das Protokoll IJobManager kann verwendet werden, um Jobs in einer Familie abzubrechen, zu verknüpfen, auf inaktiv zu setzen oder zu suchen:
IJobManager jobMan = Platform.getJobManager(); jobMan.cancel(MY_FAMILY); jobMan.join(MY_FAMILY, null);
Da Jobfamilien durch willkürliche Objekte dargestellt werden, können Sie interessante Informationen in der Jobfamilie selbst speichern, und Jobs können bei Bedarf Familienobjekte dynamisch erstellen. Es ist wichtig, dass sehr eindeutige Familienobjekte verwendet werden, damit es nicht zu einer versehentlichen Interaktion mit den von anderen Plug-ins erstellten Familien kommt.
Familien sind außerdem eine komfortable Methode, um Jobgruppen zu lokalisieren. Über die Methode IJobManager.find(Object family) können jederzeit Exemplare aller laufenden, wartenden oder inaktiven Jobs lokalisiert werden.