eclipse中的線程

我先大致講一講GUI程序中的線程.
    雖然各個操作系統之間的線程機制是不一樣的,但是大致是相同的.當用戶使用GUI程序時,如果點鼠標或按下鍵盤上的鍵等時,操作系統會產生對應的GUI事件,它來決定哪個窗口或程序來接受每一個事件並且放到程序的事件隊列中.
    任何GUI程序的底層結構就是一個事件循環.程序首先初始化事件循環,並開始循環,這個循環會從事件隊列依次接收GUI事件並一一做出相應的反應.程序應該對事件做出快速的反應使程序一直對用戶有響應,舉個例子,用戶點了一下程序裏的一個按鈕結果程序就沒反應了,那麼這個程序應該算是一個失敗的程序吧.
    如果某個UI事件引發了某個需要長時間的事務,那麼應該把它放到一個另外的單獨的線程中,這樣程序的那個事件循環就能夠馬上回來響應用戶的下一個操作.線程是非常複雜的一個主題,如果處理的不好很容易造成死鎖等很糟糕的情況.

    還好,eclipse爲我們開發插件提供了一個方便的UI線程包,大大的簡化了很多底層複雜的東西.先看看幾個簡單的概念.
1.SWT UI線程
SWT用的是操作系統直接支持的線程模式,程序會在主程序裏運行一個時間循環並依次在這個線程裏響應事件.看下面這段代碼,UI線程就是創建Display的那個線程.
public static void main (String [] args) {
    Display display = new Display ();
    Shell shell = new Shell (display);
    shell.open ();
    // 開始事件循環
    // 關掉窗口後
    while (!shell.isDisposed ()) {
      if (!display.readAndDispatch ())
        display.sleep ();
    }
    display.dispose ();
  }
簡單的小程序裏,一個UI線程就能夠滿足需要了.
但如果是長時間的操作,你就最好不要用UI線程來做這些事,可以交給Job去做.它其實就是另外啓動的線程,也就是等會我要說的非UI線程.

2.Job
Job類由org.eclipse.core.runtime插件提供.它能夠讓客戶程序員輕鬆的在另外的線程中執行代碼.
看一個小例子
Job job = new Job("My First Job") {
  protected IStatus run(IProgressMonitor monitor) {
      System.out.println("Hello World (from a background job)");
      return Status.OK_STATUS;
    }
  };
job.setPriority(Job.SHORT);
job.schedule(); // start as soon as possible

Job的默認優先級是Job.Long,這裏例子中的優先級要比它高.
只要調用Job#schedule(),它就會盡快在另外的線程中運行run()中的代碼.
再看一個小例子:
final Job job = new Job("Long Running Job") {
    protected IStatus run(IProgressMonitor monitor) {
      try {
        while(hasMoreWorkToDo()) {
          // do some work
          // ...
        if (monitor.isCanceled()) return Status.CANCEL_STATUS;
        }
        return Status.OK_STATUS;
      } finally {
        schedule(60000); // start again in an hour
      }
    }
  };
job.addJobChangeListener(new JobChangeAdapter() {
    public void done(IJobChangeEvent event) {
    if (event.getResult().isOK())
      postMessage("Job completed successfully");
      else
        postError("Job did not complete successfully");
    }
  });
job.setSystem(true);
  job.schedule(); // start as soon as possible

monitor 是一個進度顯示條,它會在運行job時自動顯示,如果任務成功運行完成,返回Status.OK_STATUS,如果中途被用戶在進度顯示條那裏中斷,就返回Status.CANCEL_STATUS.上面schedule(60000);它是讓job每過1小時就自動運行,Job又一個非常強大的功能.
然後後面是可以給job添加監聽器.
job.setSystem(true);這一句是把這個job設置爲系統級別的.如果調用setUser(true),那麼就被定義爲用戶級別的,用戶級別和默認級別的job
    在運行時會以UI形式反映出來,如果是用戶job,那麼會彈出一個進度顯示窗口,能讓用戶選擇在後臺裏運行.


再介紹job常常用到的一個方法Job#join().
系統調用到某個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;
    }
  }

  job的創建和計劃如下所示:

  TrivialJob job = new TrivialJob();
  System.out.println("About to schedule a job");
  job.schedule();
  System.out.println("Finished scheduling a job");

  他們的執行是和時間沒關係的,輸出可能如下:
  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
 

  如果希望某個job運行完成後在繼續時,可以使用join()方法.
  join()會一直阻塞到該job運行完.

例子:
  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");

上面的代碼執行後,輸出應該就是這樣:

  About to schedule a job
  This is a job
  Job completed with success

Job的功能是很強大的,還有很多功能我以後會介紹,也可以查閱官方幫助文檔.這裏先把幾個常用的問題解決掉.
參見:
http://help.eclipse.org/help30/index.jsp?topic=/org.eclipse.platform.doc.isv/guide/runtime_jobs.htm

3.如果在Job中加上改變UI的代碼就會失敗.
原因如下:
如果是在非UI線程中調用UI,SWT就會拋出一個SWTException.
要在一個非UI線程改變UI的話有幾種技術:

第一種,用:
Display#syncExec(Runnable)或
Diaplay#asyncExec(Runnable)

第二種:
已經開發了另外一種Job,就是UIJob,可以直接在它裏面運行改變UI的代碼,其實它就是在SWT的asyncExec()方法裏運行的.所有繼承UIJob的類應

該覆寫runInUIThread方法而不是run方法.

3.關於進度顯示
在Jface中:
org.eclipse.jface.operations包定義了一些接口用來在進度條下運行長時間的任務.
可以參見:
http://help.eclipse.org/help30/index.jsp?topic=/org.eclipse.platform.doc.isv/guide/jface_operations.htm

在eclipse插件和RCP開發中:
用戶級別的job是互操作性最強的,它不僅能夠讓用戶用Cancel鍵取消job,而且可以在Detail中展示具體情況,但是注意:
Detail只會在下面兩種方法中出現:
IProgressService#busyCursorWhile或
IProgressService#runInUI
1)IProgressService#busyCursorWhile的用法例子:
注意這裏的run()中做些和UI無關的事.
IProgressService progressService = PlatformUI.getWorkbench().getProgressService();
  progressService.busyCursorWhile(new IRunnableWithProgress(){
    public void run(IProgressMonitor monitor) {
      //do non-UI work
    }
  });


2)IProgressService#runInUI的用法例子:
注意這裏的run()中可以做些和UI有關的事.
progressService.runInUI(
    PlatformUI.getWorkbench().getProgressService(),
    new IRunnableWithProgress() {
      public void run(IProgressMonitor monitor) {
        //do UI work
      }
    },
    Platform.getWorkspace().getRoot());


這裏最後一個參數可以是null,或者是這個操作的規則,在這裏我們是設定運行這個UI操作時鎖定工作臺.
更加具體的可以參見:
http://help.eclipse.org/help30/index.jsp?topic=/org.eclipse.platform.doc.isv/guide/workbench_jobs.htm

另外,有少數時候,我們不想彈出一個進度條窗口,而是隻在最底下的狀態欄顯示就可以了,很簡單,寫自己的Job類時,在構造方法里加上一句:
setUser(false);就可以了.

發佈了10 篇原創文章 · 獲贊 0 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章