Silverlight使用WebService,WCF,RIA Service的同步方法

Silverlight使用WebService,WCF,RIA Service的同步方法

大多數接觸Silverlight程式開發的人只要有需要對後端Server作呼叫應該都會遇到一個問題,那就是所有的呼叫都是非同步機制,而沒有任何同步的方式,這個問題的主因在於Web機制上的問題,MSDN論壇中有很多篇已討論此問題且有介紹原因,但不幸的受限於底層.在Silverlight SDK中要做到同步會有些考量因素我想未來應該也很難加入,本篇文章在於如何解決這些問題.

由於Silverlight呼叫並不能同步呼叫所以必須找到一些方式來處理,熟悉MultiThread設計的人第一個想到的就是透過dotNet的Thread同步機制來處理,譬如說Thread.Join或AutoEvent等方式.但一使用這個方法馬上出現個一個問題,並無法正常運作似乎程式碼凍結在那邊不會執行

底下為範例程式碼


            System.Threading.AutoResetEvent evt = new System.Threading.AutoResetEvent(false);
            this.DomainContext.SubmitChanges(
               (op) =>
               {
                   evt.Set();
               }, null);
            evt.WaitOne();

程式執行到evt.WaitOne就停在那不會動了,但這個方式在WPF或Winform中運作的很順利,似乎Silverlight有個原因而無法運作,而這個原因就是Silverlight無法非同步呼叫的主因,先來看看原因,就會了解該如何解決了.
看以下程式碼



    1             this.DomainContext.SubmitChanges(
    2                (op) =>
    3                {
    4                    .....
    5                }, null);
    6 
    7             ........

以上是個一個非同步呼叫的範例程式碼,如果試著在4與7那行做中斷追蹤,會發現一個狀況,不論執行多少次,7永遠比4先執行.即使7後面還有更多的程式碼,不論如何增加,只要在同一個函式內都是一樣,故得到一個結論,在Silverlight中同一個函式內的非同步呼叫一定於函式執行完後才會執行,這也是上面evt.WaitOne會凍結在那的原因,因為evt.Set根本無會觸發.難道這個問題在所有Silverlight函式中都會發生?其實上面說法並不完全,如果在仔細閱讀MSDN論壇的原因,真正得到的結論是-

在Silverlight中UI Thread中的函式執行非UI執行緒,則此執行緒一定於函式執行完後才會執行

也就是說這只會發生在UI Thread,所以解決方式自然就出來的上述程式碼改為如下

    1             new Thread(new ThreadStart(
    2                 () => 
    3                 {
    4                 AutoResetEvent evt = new AutoResetEvent(false);
    5                 this.DomainContext.SubmitChanges(
    6                    (op) =>
    7                    {
    8                        ....
    9                        evt.Set();
   10                    }, null);
   11 
   12                 evt.WaitOne();
   13                 ........
   14             })).Start();

將原來的程式碼再包裝於另一個Thread中,即可解決此問題了.evt.WaitOne也能正常運作了.

 

 

但因為這種方式在撰寫程式時變得相當麻煩,如過更複雜些的執行順序將會導致程式在撰寫實的複雜度,故我另外也寫了一個SerialisedWorkQueue輔助類別來處理間化這個問題,使用方式如同底下程式碼,會在後續文章中介紹

           SerialisedWorkItem wi = new SerialisedWorkItem(
                (o, set) => //段落一
                {
                    this.IsBusy = true;
                    this.DomainContext.Load(this.DomainContext.GetDestributionPotentialsQuery(),
                           (op) =>
                           {
                               …
                               set();
                           }
                           , null);
                }).Parallel( 
                (o, set) => //段落二
                {
                    this.DomainContext.Load(this.DomainContext.GetDamagePotentialsQuery(),
                        (op) =>
                        {
                            
                            set();
                        }
                    , null);
                }).After( //段落三
                (o, set) =>
                {
                    this.DomainContext.Load(this.DomainContext.GetMalwareInfosQuery().Where(m => m.Id == this._malwareInfoId),
                                (op) =>
                                {
                                    
                                    this.IsBusy = false;
                                    set();
                                }
                            , null);
                });
 
            SerialisedWorkQueue workQueue = new SerialisedWorkQueue();
            workQueue.QueueWorkItem(wi);
段落一跟段落二同時執行,然後兩者執行完後再執行斷落三.