Infrastruktur for samtidighed

En af de største udfordringer i et komplekst system er være i stand til at respondere, samtidig med at der udføres opgaver. Denne udfordring er endnu større i et system, der kan udvides, hvor komponenter, der ikke er designet til at udføres samtidigt, deler de samme ressourcer. org.eclipse.core.runtime.jobs-pakken tager denne udfordring op ved at stille infrastruktur til rådighed for planlægning, udførelse og styring af funktioner, der udføres samtidigt. Denne infrastruktur er baseret på brugen af job til at repræsentere en arbejdsenhed, der kan udføres asynkront.

Job

Job-klassen repræsenterer en asynkron arbejdsenhed, der udføres samtidigt med andre job. For at en opgave kan udføres, opretter en plugin et job, som derefter planlægges. Når først et job er planlagt, tilføjes det til en jobkø, der styres af platformen. Platformen bruger en planlægningsprogramdel i baggrunden til at styre alle de ventende job. Når et igangværende job er færdigt, fjernes det fra køen, og platformen afgør, hvilket job der skal udføres som det næste. Når et job bliver aktivt, starter platformen metoden run(). Job illustreres bedst ved et eksempel:
class TrivialJob extends Job {
      public TrivialJob() {
         super("Trivial Job");
      }
         public IStatus run(IProgressMonitor monitor) {
         System.out.println("Dette er et job");
            return Status.OK_STATUS;
      }
   }
Jobbet oprettes og planlægges i følgende stykke kode:
TrivialJob job = new TrivialJob();
   System.out.println("Planlægning af job skal til at starte");
   job.schedule();
   System.out.println("Planlægning af job er afsluttet");
Outputtet fra dette program er tidsafhængigt. Det vil sige, at der er ingen måde at være sikker på, hvornår jobbets run-metode udføres i forhold til den programdel, som oprettede og planlagde jobbet. Outputtet vil enten være:
   Planlægning af job skal til at starte
   Dette er et job
   Planlægning af job er afsluttet
eller:
   Planlægning af job skal til at starte
   Planlægning af job er afsluttet
   Dette er et job

Hvis du vil være sikker på, at et job er udført, inden der fortsættes, kan du bruge metoden join(). Denne metode blokerer kalenderen, indtil jobbet er udført, eller indtil den kaldende programdel afbrydes. Vi omskriver ovenstående kodestykke, så det bliver mere handlekraftigt:

   TrivialJob job = new TrivialJob();
   System.out.println("Planlægning af job skal til at starte");
   job.schedule();
      job.join();
   if (job.getResult().isOk())
      System.out.println("Jobbet er udført");
    else
      System.out.println("Jobbet er ikke udført");
Lad os antage, at kaldet join() ikke afbrydes. Denne metode vil så returnere følgende resultat:
   Planlægning af job skal til at starte
   Dette er et job
   Jobbet er udført

Det er naturligvis ikke hensigtsmæssigt at sammenkæde et job umiddelbart efter, at du har planlagt det, da du ikke opnår samtidighed ved det. I dette tilfælde kan du lige så godt foretage arbejdet direkte fra jobbets run-metode i den kaldende programdel. I eksemplerne nedenfor gennemgås det, hvor det er nyttigt at benytte sammenkædning.

Det sidste stykke kode gør også brug af jobbets resultat. Resultatet er IStatus-objektet, som returneres fra jobbets run()-metode. Du kan bruge dette resultat til at overføre nødvendige objekter tilbage fra jobbets run-metode. Resultatet kan også bruges til at angive fejl (ved at returnere en IStatus med niveau IStatus.ERROR) eller annullering (IStatus.CANCEL).

Almindelige jobfunktioner

Det er gennemgået, hvordan et job planlægges og skal ventes på, til det er udført, men der er mange flere funktioner, der kan udføres med job. Hvis du planlægger et job, men beslutter efterfølgende, at der ikke længere er brug for det, kan jobbet stoppes vha. metoden cancel(). Hvis udførelsen af jobbet endnu ikke er startet, når det annulleres, slettes jobbet med det samme og udføres ikke. Men hvis udførelsen af jobbet allerede er startet, er det jobbet, der afgør, om der skal reageres på annulleringen. Når du forsøger at annullere et job, kan det være nyttigt at vente på det vha. metoden join(). I det følgende annulleres et job, og der ventes, indtil jobbet er afsluttet, inden der fortsættes:

   if (!job.cancel())
      job.join();

Hvis annulleringen ikke finder sted med det samme, returnerer cancel() false, og kalderen bruger join() til at vente på, at jobbet annulleres.

Metoden sleep() er noget mindre dramatisk end annullering. Hvis udførelsen af jobbet endnu ikke er startet, bevirker denne metode, at jobbet tilbageholdes i det uendelige. Jobbet huskes stadig af platformen, og et kald af typen wakeUp() bevirker, at jobbet tilføjes til ventekøen, hvorfra det vil blive udført.

Jobtilstande

I løbet af et jobs levetid gennemgår det flere tilstande. Det kan ikke blot manipuleres via API, f.eks. cancel() og sleep(), men dets tilstand ændres også, når platformen udfører og gennemfører jobbet. Job kan passere igennem flere tilstande:

Et job kan kun sættes i dvaletilstand fra tilstanden WAITING. Når et job i dvaletilstand vækkes, sættes det i WAITING-tilstand. Annullering af et job sætter det i tilstanden NONE.

Hvis plugin'en har brug for at kende tilstanden af et bestemt job, kan den registrere en jobændringslytter, der underrettes, når jobbet bevæger sig gennem sin livscyklus. Denne funktion er nyttig til at vise status og til at rapportere om et job.

Jobændringslyttere

Jobmetoden addJobChangeListener kan bruges til at registrere en lytter for et bestemt job. IJobChangeListener definerer protokollen til at reagere på jobbets tilstandsændringer:

I alle disse tilfælde stilles en IJobChangeEvent til rådighed for lytteren. Og denne angiver det job, hvis tilstand er ved at ændres og jobbets status ved afslutning (hvis det udføres).

Bemærk: Job definerer også metoden getState() til brug for hentning af den (relative) nuværende tilstand af et job. Men dette resultat er ikke altid pålideligt, da job udføres i forskellige programdele og kan allerede have ændret tilstand igen, når kaldet returneres. Jobændringslyttere er den anbefalede mekanisme til registrering af jobtilstandsændringer.

Jobstyringsfunktion

IJobManager definerer protokollen for arbejdet med alle job i systemet. Plugins, der viser status, eller som ellers arbejder med jobinfrastrukturen, kan bruge IJobManager til at udføre opgaver, f.eks. tilbageholde alle job i systemet, undersøge, hvilke job der udføres, eller modtage feedback om status vedr. et bestemt job. Platformens jobstyringsfunktion kan hentes vha. platform-API'et:

      IJobManager jobMan = Platform.getJobManager();

Plugins, der er interesseret i tilstanden af alle job i systemet, kan registrere en jobændringslytter for jobstyringsfunktionen i stedet for at registrere lyttere på flere individuelle job.

Jobfamilier

Det kan til tider være nemmere for en plugin at arbejde med en gruppe relaterede job som en enkelt enhed. Dette opnås vha. jobfamilier. Et job erklærer, at det tilhører en bestemt familie ved at tilsidesætte belongsTo-metoden:

   public static final String MY_FAMILY = "myJobFamily";
   ...
   class FamilyJob extends Job {
      ...
      public boolean belongsTo(Object family) {
         return family == MY_FAMILY;
      }
   }
IJobManager-protokollen kan bruges til at annullere, sammenkæde, sætte i dvaletilstand eller finde alle job i en familie:
   IJobManager jobMan = Platform.getJobManager();
   jobMan.cancel(MY_FAMILY);
   jobMan.join(MY_FAMILY, null);

Da jobfamilier repræsenteres vha. vilkårlige objekter, kan du gemme interessante tilstande i jobfamilien, og job kan bygge familieobjekter dynamisk, hvis det er behov for det. Det er vigtigt at bruge familieobjekter, der er forholdsvis entydige, så utilsigtet interaktion med de familier, der er oprettet af andre plugins, undgås.

Familier er også en nyttig måde at placere grupper af job på. Metoden IJobManager.find(Objektfamilie) kan bruges til at finde forekomster af alle igangværende og ventende job og job i dvaletilstand på et hvilket som helst tidspunkt.