TWaver - 專注UI技術(shù)

          http://twaver.servasoft.com/
          posts - 171, comments - 191, trackbacks - 0, articles - 2
            BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

          深入淺出GUI線程安全(三)

          Posted on 2010-09-03 14:10 TWaver 閱讀(1811) 評(píng)論(0)  編輯  收藏
          第一第二篇后,GUI線程安全的原理性內(nèi)容基本就這些了,如果你是搞學(xué)術(shù)理論研究的基本就不用繼續(xù)閱讀下面幾篇我要繼續(xù)八卦的內(nèi)容,下面的內(nèi)容都是針對具體技術(shù)平臺(tái)的細(xì)節(jié)問題了。
          1static void Main(string[] args)
          2{
          3    TextBlock text = new TextBlock();
          4}

          創(chuàng)建個(gè)Console程序,敲出上面的和helloworld一樣復(fù)雜度的代碼,運(yùn)行后你估計(jì)會(huì)懷疑自己的智商,這么簡單的代碼都能搞出異常?

          還真是再簡單問題都有其復(fù)雜的一面,我總在想為什么傻人有傻福,因?yàn)樯等松瞪档恼J(rèn)真對待每個(gè)簡單的事情所以搞透了其復(fù)雜的本質(zhì),所謂的聰明人都去研究哥德巴赫猜想,日積月累傻人掌握了眾多事物本質(zhì)成了有福之人,所謂聰明者竟然沒搞明白一加一為什么等于二。

           1private InputManager()
           2{
           3    // STA Requirement
           4    //
           5    // Avalon doesn't necessarily require STA, but many components do.  Examples
           6    // include Cicero, OLE, COM, etc.  So we throw an exception here if the
           7    // thread is not STA.
           8    if(Thread.CurrentThread.GetApartmentState() != ApartmentState.STA)
           9    {
          10        throw new InvalidOperationException(SR.Get(SRID.RequiresSTA));
          11    }

          看看System.Windows.Input.InputManager的代碼實(shí)現(xiàn)你就明白了,原來TextBlock的創(chuàng)建要求在STA環(huán)境中,你要問我啥叫STA,我喜歡將其簡單理解為這玩意兒就是我們前面提到的EDT,也就是那個(gè)全局唯一的UI Thread,具體的解釋你可以看Component_Object_Model#Threading_in_COM的解釋,當(dāng)年讀潘愛民翻譯的《COM本質(zhì)論》差點(diǎn)讓我沒了繼續(xù)當(dāng)程序員的勇氣,以下引用了一段話你就看著玩就行

           
          There are three types of Apartment Models in the COM world: Single-Threaded Apartment (STA), Multi-Threaded Apartment (MTA), and Neutral Apartment. Each apartment represents one mechanism whereby an object’s internal state may be synchronized across multiple threads.
          The Single-Threaded Apartment (STA) model is a very commonly used model. Here, a COM object stands in a position similar to a desktop application’s user interface. In an STA model, a single thread is dedicated to drive an object’s methods, i.e. a single thread is always used to execute the methods of the object. In such an arrangement, method calls from threads outside of the apartment are marshalled and automatically queued by the system (via a standard Windows message queue). Thus, there is no worry about race conditions or lack of synchronicity because each method call of an object is always executed to completion before another is invoked. 

          上面的一行代碼其實(shí)已經(jīng)說明了問題Thread.CurrentThread.GetApartmentState() != ApartmentState.STA,也就是main啟動(dòng)運(yùn)行的線程并不是EDT,回想一下WindowsForms和WPF的應(yīng)用程序的main是怎么寫的,是的就是這個(gè)[STAThread]起的作用,把上面代碼加上這個(gè)[STAThread]你就不會(huì)再懷疑自己智商了。

          1[STAThread]
          2static void Main()
          3{
          4     
          5}

          《Applications = Code + Markup》這本是我學(xué)習(xí)WPF的第一本書,講解思路非常符合傳統(tǒng)程序員口味,上半本先完全用OO的方式解釋了WPF組件的來龍去脈,下半本才開始設(shè)計(jì)XAML的標(biāo)簽應(yīng)用,說實(shí)話我很看不慣現(xiàn)在的快餐式書籍,一上來就是XAML用戶連WPF組件Dependency Properties這種最重要的新機(jī)制都完全不明白的情況下就開始寫項(xiàng)目,同樣的還有一上來就開始鼓吹如何用MXML快速創(chuàng)建Flex程序,本末倒置不介紹背后ActionScript語言實(shí)現(xiàn)原理的書籍,更有一大堆歷史杯具人物寫了多年JSP不知道啥是Java的OO。

          前幾天逛書店發(fā)現(xiàn)蔡學(xué)庸也翻譯了《Applications = Code + Markup》一書,剛畢業(yè)時(shí)買過他的一本經(jīng)典《Java夜未眠》,那時(shí)候作為個(gè)junior程序員的我手頭也幾個(gè)錢,晚上也沒法去新天地泡mm的方式夜未眠,只好翻翻學(xué)庸兄的《Java夜未眠》,不過這些年薪水提高了反而不去書店買書了,原因很簡單譯書上得不到最新的技術(shù)資料而且翻譯往往后失去作者的原味,相信《紅樓夢》的譯書很難接近原著的味道,扯遠(yuǎn)了,我不是要推銷《Applications = Code + Markup》,更不是要打擊蔡學(xué)庸譯書的銷量(我還是很尊重且喜歡訂閱學(xué)庸兄的blog,如果學(xué)庸看到小弟此文希望能有機(jī)會(huì)交個(gè)朋友),只是想引用書中一段話幫助大家理解:

          In any WPF program, the [STAThread] attribute must precede Main or the C# compiler will complain. This attribute directs the threading model of the initial application thread to be a single-threaded apartment, which is required for interoperability with the Component Object Model (COM). “Single-threaded apartment” is an old COM-era, pre-.NET programming term, but for our purposes you could imagine it to mean our application won’t be using multiple threads originating from the runtime environment.

          使用TWaver Java的客戶應(yīng)該有印象,TWaver Java Demo的啟動(dòng)代碼和地球上99%的Swing的main代碼不一樣,套了個(gè)SwingUtilities.invokeLater,曾經(jīng)有個(gè)客戶程序啟動(dòng)再大部分機(jī)器上都正常,結(jié)果在一臺(tái)雙核(N年前雙核可是很洋相配置)的機(jī)器啟動(dòng)總出錯(cuò),去現(xiàn)場幫忙找原因時(shí)我查的第一行代碼就是main的啟動(dòng)代碼,發(fā)現(xiàn)用戶直接就在main里面創(chuàng)建各種UI組件,同時(shí)還起了Thread做各種業(yè)務(wù),我就讓他改成和TWaver Demo一樣,啟動(dòng)時(shí)套了個(gè)SwingUtilities.invokeLater,你猜怎么樣我竟然“被”允許回家了,這是我在賽瓦這些年最短的現(xiàn)場支持經(jīng)歷,像我這種不敢坐飛機(jī)的(去年做了38個(gè)小時(shí)火車去了哈爾濱培訓(xùn)客戶)做了十幾個(gè)小時(shí)的火車竟然現(xiàn)場待了幾分鐘就回家了,實(shí)在是虧啊。

          1public static void main(String[] args) {
          2    SwingUtilities.invokeLater(new Runnable(){
          3        public void run() {
          4            Demo.init();
          5            DemoFrame demoFrame = new DemoFrame();
          6            demoFrame.setVisible(true);
          7        }

          8    }
          );
          9}

          很簡單,因?yàn)閙ain啟動(dòng)的函數(shù)并不是Swing的事件派發(fā)線程,SwingUtilities.isEventDispatchThread()你可以調(diào)用該函數(shù)測試一下,而地球99%的代碼直接非EDT里面做UI的工作時(shí)極其危險(xiǎn)的,大部分程序都是初始化界面然后show就結(jié)束了,這樣基本能僥幸不出錯(cuò),但如果你調(diào)用了show之后再有代碼在執(zhí)行,而且是操作到UI組件的地方拿基本必然會(huì)導(dǎo)致問題,但為了您的生命安全還請遵守GUI線程安全規(guī)則。這里有更多的解釋可以參考:

          public class MyApplication {
          public static void main(String[] args) {
          JFrame f = new JFrame(“Labels”);
          // Add components to
          // the frame here…
          f.pack();
          f.show();
          // Don’t do any more GUI work here…
          }
          }
          All the code shown above runs on the “main” thread. The f.pack() call realizes the components under the JFrame. This means that, technically, the f.show() call is unsafe and should be executed in the event-dispatching thread. However, as long as the program doesn’t already have a visible GUI, it’s exceedingly unlikely that the JFrame or its contents will receive a paint() call before f.show() returns. Because there’s no GUI code after the f.show() call, all GUI work moves from the main thread to the event-dispatching thread, and the preceding code is, in practice, thread-safe. 

          另外我挺喜歡現(xiàn)在.NET的線程安全問題上再底層框架做的檢查工作,就像剛才Console操作TextBlock的代碼,很容易程序員就能知道問題所在,而Swing這點(diǎn)上是最讓我不滿意的地方,你不提示檢查我這些在普通線程操作UI組件就算了,某些情況下Swing還自作聰明的在內(nèi)部幫你SwingUtilities.invoke***,結(jié)果常常導(dǎo)致很多客戶質(zhì)問我為什么我在普通線程調(diào)用revalidate()不出錯(cuò),你們還非得讓我以這么丑陋的代碼方式到處去SwingUtilities.invoke,我覺得Swing這樣的實(shí)現(xiàn)會(huì)誤導(dǎo)程序員放松對線程安全的戒備,Swing僅僅在很少很少很少的函數(shù)上做了判斷內(nèi)部處理,如果大家覺得JComponent.revalidate()可以隨意調(diào)用,那豈不TableModel、TreeModel…包括TWaver的DataBox和Element都可以不管EDT隨意調(diào)用了,所以這點(diǎn)上.NET嚴(yán)謹(jǐn)?shù)呐袛噙€是能讓程序員盡早的規(guī)避了很多線程安全問題

           1public void revalidate() {
           2    if (getParent() == null{
           3        // Note: We don't bother invalidating here as once added
           4        // to a valid parent invalidate will be invoked (addImpl
           5        // invokes addNotify which will invoke invalidate on the
           6        // new Component). Also, if we do add a check to isValid
           7        // here it can potentially be called before the constructor
           8        // which was causing some people grief.
           9        return;
          10    }

          11    if (SwingUtilities.isEventDispatchThread()) {
          12        invalidate();
          13        RepaintManager.currentManager(this).addInvalidComponent(this);
          14    }

          15    else {
          16        Runnable callRevalidate = new Runnable() {
          17            public void run() {
          18                revalidate();
          19            }

          20        }
          ;
          21        SwingUtilities.invokeLater(callRevalidate);
          22    }

          23}
          今天要講的就這些內(nèi)容,不知道昨天提到TWaver的FileTreeDemo里面還有個(gè)多線程的細(xì)節(jié),你有沒有注意到tree在加載文件是還能看到gif的loading圖標(biāo),你只要打開tree.setEnableAnimation(true)開關(guān),隨時(shí)隨意間任何element.setIcon(“/demo/tree/file/loading.gif”)設(shè)置上任何gif圖標(biāo),twaver將透明自動(dòng)的識(shí)別gif動(dòng)畫圖標(biāo),并且讓tree上的icon動(dòng)畫起來。這還沒啥,有一回我看到用戶啟動(dòng)畫面竟然有很酷動(dòng)畫的splash screen,我問用戶是不是嵌入了Flash,用戶說:“用的就是你們的Network做啟動(dòng)界面,只不過放了一個(gè)GIF的大Node做主啟動(dòng)界面而已,賽瓦真該開除了你這個(gè)傻B,還號(hào)稱TWaver Evangelist,丟人也不能丟這么大啊”

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


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 海城市| 罗源县| 玉山县| 九龙城区| 瓦房店市| 皮山县| 华阴市| 阳城县| 纳雍县| 宝鸡市| 朝阳区| 定远县| 上饶县| 苍山县| 南陵县| 宜阳县| 广东省| 兴宁市| 淮阳县| 诸城市| 宁武县| 木里| 师宗县| 松溪县| 湟源县| 普宁市| 宁城县| 班戈县| 哈尔滨市| 怀仁县| 冷水江市| 双辽市| 都匀市| 蛟河市| 卢氏县| 大方县| 莫力| 武夷山市| 三台县| 拜城县| 高雄县|