並行基礎架構

複式系統的主要挑戰之一是在執行作業時,保持回應能力。當不是為了一起執行而設計的元件共用相同資源時,在可延伸系統中,這項挑戰甚至更大。org.eclipse.core.runtime.jobs 套件藉由提供排定、執行和管理並行執行作業的基礎架構來面對這項挑戰。這個基礎架構的基礎在於利用工作來代表能夠非同步執行的工作單元。

工作

Job 類別代表與其他工作並行執行的非同步工作單元。如果要執行某項作業,外掛程式會先建立一個工作,再排定這項工作。工作排定之後,就會將它加到平台所管理的工作佇列中。平台會利用背景排程執行緒來管理所有擱置中的工作。當執行中的工作完成時,會將它從佇列中移除,平台會決定接著所要執行的工作。當工作成為作用中,平台會呼叫它的 run() 方法。下列簡單範例是工作的最佳示範:
   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;
      }
   }
下列片段用來建立和排定工作:
   TrivialJob job = new TrivialJob();
   System.out.println("About to schedule a job");
   job.schedule();
   System.out.println("Finished scheduling a job");
這個程式的輸出會隨著時機而不同。也就是說,無法確定工作的 run 方法何時會相關於建立和排定工作的執行緒來執行。輸出將是:
   About to schedule a job
   This is a job
   Finished scheduling a job
或:
   About to schedule a job
   Finished scheduling a job
   This is a job

如果在繼續作業之前,您要確定某項工作已經完成,您可以使用 join() 方法。這個方法會暫停執行呼叫端,直到工作完成為止,或直到發出呼叫的執行緒岔斷為止。我們以更具決定性的方式來重新撰寫上述片段:

      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");
假設沒有岔斷 join() 呼叫,可以確保這個方法會傳回下列結果:
   About to schedule a job
   This is a job
   Job completed with success

當然,一般而言,排定工作之後立即合併工作並沒有用,因為這並不會帶來並行作業的結果。在這個情況下,您也可以直接在發出呼叫的執行緒中,從工作的 run 方法執行作業。稍後,我們將更詳細檢視一些在哪些地方運用合併會更有意義的範例。

最後一個片段也使用 result 工作。結果是從工作的 run() 方法傳回的 IStatus 物件。您可以利用這個結果,從工作的 run 方法傳回任何必要的物件。您也可以利用這個結果來指示失敗(傳回嚴重性為 IStatus.ERRORIStatus)或取消 (IStatus.CANCEL)。

一般工作作業

我們已經看過如何排定工作和等待它完成,但您還可以執行許多其他感興趣的作業。如果您排定了某項工作之後,又認定不再需要它,您可以利用 cancel() 方法來停止工作。如果取消時,工作還沒有開始執行,就會立即捨棄工作,不會執行它。另一方面,如果工作已開始執行,就由工作來決定是否要回應取消作業。當您試圖取消工作時,利用 join() 方法來等待它會很方便。以下是取消工作,等工作完成再繼續作業的慣用句:

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

如果取消沒有立即生效,cancel() 會傳回 false,呼叫端會利用 join() 來等待工作順利取消。

sleep() 方法比取消稍微緩和。同樣地,如果工作還沒有開始執行,這個方法會使工作無限地保留。平台仍會記住這個工作,wakeUp() 呼叫會將工作加到等待佇列中,最終仍會執行這個工作。

工作狀態

工作的生命週期會經歷許多狀態。它不但能利用 cancel()sleep() 等 API 來操作, 當平台執行和完成工作時,它的狀態也會隨之變更。工作經歷的狀態如下:

工作目前必須是 WAITING,才能進入休眠狀態。起動休眠的工作會使工作返回 WAITING 狀態。取消工作會使工作返回 NONE 狀態。

如果您的外掛程式需要知道特定工作的狀態,它可以登錄一個工作變更接聽器, 當工作在生命週期中移動時,這個接聽器會收到通知。這可用來顯示工作進度或提出工作報告。

工作變更接聽器

Job 方法 addJobChangeListener 可用來登錄特定工作的接聽器。IJobChangeListener 定義回應工作狀態變更的通訊協定:

所有這些情況都會向接聽器提供 IJobChangeEvent,以指定經歷狀態變更的工作及工作完成時的狀態(如果完成的話)。

附註:工作也會定義用來取得工作(相對)現行狀態的 getState() 方法。不過,這個結果不一定可靠,因為工作是在不同執行緒中執行的,當呼叫傳回時,狀態可能會再次變更。工作變更接聽器是建議用來探索工作狀態變更的機制。

工作管理程式

IJobManager 定義用來處理系統中所有工作的通訊協定。顯示進度或處理工作基礎架構的外掛程式可以利用 IJobManager 來執行各種作業, 如暫停系統中所有工作、尋找執行中的工作或接收特定工作之進度回饋等。平台的工作管理程式也可以利用 Platform API 來取得:

      IJobManager jobMan = Platform.getJobManager();

想要知道系統中所有工作狀態的外掛程式,可以在工作管理程式上登錄一個工作變更接聽器,而不是在許多個別工作上登錄接聽器。

工作系列

對外掛程式而言,有時將相關工作群組當作單一單元來使用會比較容易。您可以利用工作系列來做到這一點。工作會置換 belongsTo 方法來宣告它屬於某個系列:

   public static final String MY_FAMILY = "myJobFamily";
   ...
   class FamilyJob extends Job {
      ...
      public boolean belongsTo(Object family) {
         return family == MY_FAMILY;
      }
   }
IJobManager 通訊協定可用來取消、合併、休眠或尋找系列中的所有工作:
   IJobManager jobMan = Platform.getJobManager();
   jobMan.cancel(MY_FAMILY);
   jobMan.join(MY_FAMILY, null);

由於工作系列是用任意物件來表示的,因此,您可以將感興趣的狀態儲存在工作系列中,且工作可以依照需要來動態建置系列物件。請務必使用專屬的系列物件,以避免與其他外掛程式所建立的系列造成意外的互動。

系列也是尋找工作群組的便利方式。您可以在任何給定的時間,利用 IJobManager.find(Object family) 方法來尋找所有執行中、等待中和休眠中工作的實例。