隨筆-10  評論-11  文章-20  trackbacks-0
             在使用SWT構(gòu)建應(yīng)用程序時,理解系統(tǒng)底層讀取和調(diào)度平臺GUI事件的線程模型是非常重要的,UI線程的實現(xiàn)方式會影響到在應(yīng)用程序中使用 Java 線程時必須遵守的規(guī)則。

           

          本地事件調(diào)度

                  對于任何的GUI應(yīng)用程序,不管所使用的是哪一種編程語言和UI工具包,背后的運行機制都是操作系統(tǒng)探測GUI事件并把它們放到應(yīng)用程序的事件隊列中去。這種機制在不同的操作系統(tǒng)平臺中大同小異。當用戶點擊鼠標、鍵入字符、或者使窗口獲得焦點,操作系統(tǒng)就會生成應(yīng)用程序的GUI事件,例如鼠標點擊、鍵盤輸入、或窗口重繪事件。操作系統(tǒng)決定哪一個窗口和應(yīng)用程序應(yīng)該接收用戶觸發(fā)的每一個事件并把它放入應(yīng)用程序的事件隊列中。

           

                  任何基于窗口的GUI應(yīng)用程序的底層實現(xiàn)結(jié)構(gòu)都是一個事件循環(huán)(event loop),應(yīng)用程序初始化并運行一個循環(huán)(loop),用于從事件隊列中讀取GUI事件,并作出相應(yīng)的反應(yīng)。處理事件的工作必須迅速完成,以保證GUI應(yīng)用程序能夠?qū)τ脩糇鞒隹焖俜磻?yīng)。

           

                  UI事件觸發(fā)的耗時較長的操作應(yīng)該在一個單獨的線程中執(zhí)行,這樣才能讓事件循環(huán)主線程能夠快速返回,獲取應(yīng)用程序事件隊列中的下一個事件。但是,在非UI線程中訪問圖形界面部件和平臺API 必須通過鎖和串行化的機制(locking and serialization)來實現(xiàn)。違反這個規(guī)則的應(yīng)用程序會引起系統(tǒng)調(diào)用失敗,更嚴重的是鎖住整個GUI系統(tǒng),使GUI失去反應(yīng)。

           

          SWT UI 線程

                  SWT 遵循系統(tǒng)平臺所直接支持的這種線程模型,應(yīng)用程序在它的主線程中運行事件循環(huán)(event loop),并在主線程中直接調(diào)度線程。UI線程就是Display 對象被創(chuàng)建的線程,所有其他的圖形部件都必須在這個UI線程中創(chuàng)建。

                  既然所有的處理事件的代碼是在應(yīng)用程序的UI線程中觸發(fā)的,那么處理事件的程序代碼就能夠不需要任何特殊方法自由訪問和調(diào)用圖形部件。不過,在處理長耗時的事件操作時,需要使用多線程來實現(xiàn)應(yīng)用程序的功能。

           

          注:在非UI線程中調(diào)用任何必須在UI線程調(diào)用的程序,SWT將會觸發(fā)一個 SWTException 異常。

           

          SWT 應(yīng)用程序的主線程,包括事件循環(huán),其代碼結(jié)構(gòu)如下:

          Java代碼 復(fù)制代碼 收藏代碼
          1. public static void main (String [] args) {   
          2.    Display display = new Display ();   
          3.    Shell shell = new Shell (display);   
          4.    shell.open ();   
          5.    // start the event loop. We stop when the user has done   
          6.    // something to dispose our window.   
          7.    while (!shell.isDisposed ()) {   
          8.       if (!display.readAndDispatch ())   
          9.          display.sleep ();   
          10.    }   
          11.    display.dispose ();   
          12. }  

                  創(chuàng)建圖形部件和打開shell 窗口之后,程序讀取和分發(fā)來自操作系統(tǒng)事件隊列的事件,直到shell 窗口被銷毀。如果在隊列中不存在有效事件,display 進入睡眠狀態(tài),把運行機會交給其他程序。

           

                  SWT 提供了在后臺主線程中調(diào)用圖形部件代碼的訪問方法。

           

          運行非UI線程

                   在非UI 線程中不能直接調(diào)用UI 代碼,必須提供一個 Runnable對象,在Runable中調(diào)用UI代碼。Display 類中的syncExec(Runnable) 和 asyncExec(Runnable) 方法用于在事件循環(huán)運行期間,在UI 線程中運行這些Runnable對象。

          • syncExec(Runnable)  當非UI 線程中的程序代碼依賴于UI 代碼的返回值,或者為了確保在返回到主線程之前Runnable 必須執(zhí)行完成時,應(yīng)該使用這個方法。SWT 將會阻塞調(diào)用線程,直到在應(yīng)用程序的UI 線程中運行的這個Runnable運行結(jié)束為止。例如,一個后臺線程需要基于一個窗口的當前尺寸進行某種計算,就會需要同步地運行獲取窗口尺寸的代碼,然后繼續(xù)其后面的計算。
          • asyncExec(Runnable)  當程序需要執(zhí)行一些UI 操作,但在繼續(xù)執(zhí)行之前不依賴這些操作必須完成的時候,應(yīng)該使用這個方法。例如,后臺主線程更新進度條,或者重繪一個窗口,它可以異步地發(fā)出更新或重繪的請求,并接著繼續(xù)后面的處理,在這種情況下,后臺主線線程的運行時間和Runnable的運行沒有必然的關(guān)系。
          下面的代碼片段演示了使用這兩個方法的方式:
          Java代碼 復(fù)制代碼 收藏代碼
          1. // do time-intensive computations   
          2. ...   
          3. // now update the UI. We don't depend on the result,   
          4. // so use async.   
          5. display.asyncExec (new Runnable () {   
          6.    public void run () {   
          7.       if (!myWindow.isDisposed())   
          8.          myWindow.redraw ();   
          9.    }   
          10. });   
          11. // now do more computations   
          12. ...  
                  在使用asyncExec 的時候,在runnable 中檢查圖形部件是否被銷毀是一個好的習(xí)慣做法,在調(diào)用asyncExec和Runnable執(zhí)行期間主線程中有可能會發(fā)生其他的事情,不能保證runnable執(zhí)行時圖形部件當前處于什么狀態(tài)。

          工作臺(Workbench)和多線程

                  實現(xiàn)SWT應(yīng)用程序的多線程規(guī)則非常明確,你可以控制事件循環(huán)的初始化,在應(yīng)用程序中使用多線程解決復(fù)雜問題。

           

                  向工作臺添加插件時的工作機制要父子一些,下面是使用平臺(workbench platform)UI 類的一些“規(guī)約”(Rules of engagement),隨著eclipse 的不斷發(fā)布,可能會出現(xiàn)一些例外:

          • 通常,任何添加到平臺中的工作臺(workbench) UI 擴展都是在工作臺的UI 主線程中執(zhí)行的,除非是明確地把它們添加線程中或者是后臺作業(yè)(background job)中,例如后臺作業(yè)進度條。
          • 如果從工作臺接收到一個事件,不能保證它是在UI線程中執(zhí)行的,查閱定義了監(jiān)聽器或事件的類的java文檔,如果沒有特別說明使用線程,這個類就是一個UI 相關(guān)類,可以在工作臺主線程中獲得和運行。
          • 同樣,除非是文檔明確說明,平臺UI庫不能視作是線程安全的。請注意,大部分平臺UI類是在觸發(fā)事件的調(diào)用線程中運行監(jiān)聽器的,平臺和JFace API調(diào)用并不檢查是在UI 線程中執(zhí)行的,這意味著如果在非UI 線程中調(diào)用一個能夠觸發(fā)事件的方法,可能會引入問題。從非UI 線程中調(diào)用SWT的API,SWT會拋出 SWTException 異常。通常,除非文檔中明確規(guī)定,避免在別的線程中調(diào)用平臺UI 代碼。
          • 如果你的插件使用多線程或工作臺作業(yè)(workbench job),必須使用 Display 類的asyncExec(Runnable) 或 syncExec(Runnable) 方法,類調(diào)用任何的工作臺(workbench)、JFace或SWT 的應(yīng)用程序接口(API),除非是API明確說明是可以直接調(diào)用的。
          • 如果在插件中使用 JFace的IRunnableContext 接口 調(diào)用進度監(jiān)視器(progress monitor),以運行一個操作,IRunnableContext 提供了一個參數(shù)來確定是不是在一個新的線程中運行操作。
          附:
          參考:http://help.eclipse.org/helios/index.jsp?topic=/org.eclipse.platform.doc.isv/guide/swt_threading.htm
          posted on 2011-04-17 21:58 Soap MacTavish 閱讀(1488) 評論(0)  編輯  收藏

          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 凯里市| 濮阳县| 望谟县| 台北县| 东丽区| 潼南县| 乡宁县| 龙门县| 苏尼特右旗| 墨竹工卡县| 芦山县| 武乡县| 即墨市| 陆河县| 六盘水市| 调兵山市| 新干县| 孟连| 南江县| 民县| 大丰市| 巩留县| 阳朔县| 宾川县| 神农架林区| 莒南县| 清水河县| 安庆市| 恭城| 三明市| 龙胜| 上饶县| 渭南市| 永泰县| 水城县| 雷山县| 青河县| 体育| 大兴区| 延寿县| 盐城市|