2010-02-21 52 views
6

我有如下一些代碼:的Silverlight,處理異步調用

 foreach (var position in mAllPositions) 
     { 
       DoAsyncCall(position); 
     } 
//I want to execute code here after each Async call has finished 

所以,我該怎麼辦上面?
我可以這樣做:

 while (count < mAllPositions.Count) 
     { 
      //Run my code here 
     } 

和增量次數每個異步調用後,由...但是這似乎並不像做

任何意見的好辦法?是否有上述問題的設計模式,因爲我確定這是一種常見的情況?

回答

0

(NB,我給你兩個不同的答案,這一個堅持儘可能靠近可能你的問題。)

你的問題說

while{ count >= mAllPositions.Count) 
{ 
    //Run my code here 
} 

,但我猜那是你真正的意思是:

while(count < mAllPositions.Count) 
    ; // do nothing -- busy wait until the count has been incremented enough 
// Now run my code here 

?? ??

如果是這樣,那麼你就可以更有效地利用旗語完成同樣的事情:

Semaphore TheSemaphore = new Semaphore(0, mAllPositions.Count); 

由於每個異步調用完成,釋放信號量。

TheSemaphore.Release(); 

之前執行你的最終代碼,保證了信號已被釋放的時候必需數量:

for(int i=0; i<mAllPositions.Count; i++) 
    TheSemaphore.WaitOne(); 
// Now run follow-on code. 

您的代碼將被阻塞,直到異步操作已經全部完成。

+0

Silverlight上的信號量? –

0

你的問題有點不清楚(應該是count <=?),但在這裏不用...

你問的是如何做一個同步電話。通過異步呼叫,在您撥打電話之前,您可以分配一個事件處理程序,以在呼叫完成時被呼叫,然後您撥打電話。這意味着您的完成代碼與撥打電話的代碼具有不同的功能。這意味着如果在UI線程上進行了異步調用,則在調用進行時UI不會阻塞。

在Silverlight中同步調用是可能的,但您必須確保您不會在UI線程上執行同步調用。實現此目的的一種方法是啓動一個新的後臺線程,在後臺線程上執行異步調用,但阻止返回,直到調用完成。下面是一個僞代碼示例:

private AutoResetEvent myResetEvent; 

private void MyCallFunction(object someParameter) { 

    if (this.Dispatcher.CheckAccess()) 
    { 
     Action<object> a = new Action<object>(MyCallFunction); 
     a.BeginInvoke(someParameter, null, null); 
     return; 
    } 

    myResetEvent = new AutoresetEvent(); 
    myAsyncCall.CallCompleted += new EventHandler<>(myAsyncCall_CallCompleted); 
    myAsyncCall.DoAsyncCall(someParameter); 

    myResetEvent.WaitOne(); 
    //increment your count here 
} 

private void myAsyncCall_CallCompleted(object sender, SomeEventArgs e) { 
    if (e.Error == null && !e.Cancelled) { 
     if (myResetEvent != null) 
      myResetEvent.Set(); 
    } 
} 

注意,這個代碼不是特別線程安全或生產準備 - 這僅僅是一個快速的樣品。

這裏發生的是當你輸入MyCallFunction時,它會檢查它是否在UI線程上運行,如果它是在後臺線程上重新調用它自己的話。然後它設置一個AutoResetEvent,並進行異步調用。然後,它暫停myResetEvent對象,直到它已經從調用完成處理程序中設置(或「發送」)爲止,此時代碼繼續執行。請注意,您不應該嘗試直接從這樣的代碼訪問控件,而無需首先確保您返回到UI線程。

當我開始研究如何在Silverlight中執行此操作時,我從this SO post開始,繼續使用this link。但正如Marc gravell在第一篇文章中所說,如果你能避免它,就不要這樣做。我需要這樣做的唯一時間是當我需要將幾個完全不同的WCF調用的結果聚合成一個返回到UI的結果(並且這些調用不能合併到一個正面樣式的WCF方法中)時。

0

(NB,我給兩個不同的答案,這需要一個完全不同的方法比問題。)

this URL米格爾·馬德羅的代碼示例,使用無框架並行等待所有結果完成,然後繼續完成另一項任務。

(有沒有在相同的電子郵件鏈中的其他討論,其中包括一個鏈接到this closely related StackOverflow question。)

0
  1. 設置一個靜態的AutoResetEvent
  2. 設置ThreadPoolQueueUserWorkItems在循環
  3. 的ForEach項目,排隊的項目,並在通過的AutoResetEvent作爲回調的參數。
  4. 在回調中,執行該工作,然後調用autoresetevent.Set();
  5. 在主體//I want to execute code here after each Async call has finished處調用相應的autoResetEvent.Wait();
0

另一種方法是在調用異步進程的foreach循環中增加一個計數器。然後在回調檢查中,您將遞減計數器並檢查相等爲零。當計數器返回到零時,所有的通話都完成,你的下一個代碼可以運行。只要確保您以線程安全的方式更改計數器即可。執行此操作的最簡單方法可能是在遞減並測試計數器之前返回到UI線程。

0

我不認爲這是「我想做同步呼叫」的問題。當你全部完成時,你真正要做的就是將一系列異步調用聯繫在一起。

我曾遇到過這種情況,你有一個項目列表,並需要從服務器異步獲取它們的屬性(例如一些狀態),但也想更新屏幕上的一些加載進度指示器。

在這種情況下,您不希望在更新所有項目時阻塞UI線程(如果有其他選擇,阻塞線程也是邪惡的)。

所以我用一個控制類是這樣的:

public class GroupAction 
{ 
    public delegate void ActionCompletion(); 

    private uint _count = 0; 
    private ActionCompletion _callback; 
    private object _lockObject = new object(); 

    public GroupAction(uint count, ActionCompletion callback) 
    { 
     _count = count; 
     _callback = callback; 
    } 

    public void SingleActionComplete() 
    { 
     lock(_lockObject) 
     { 
      if (_count > 0) 
      { 
       _count--; 
       if (_count == 0) _callback(); 
      } 
     } 
    } 
} 

您創建一個計數(用於項目的數量),並且得到的所有項目完成後執行的回調這個動作。基本上就像一個控制更多,沒有線程擔心的信號量。

調用代碼看起來像這樣(我已經根據調用標準臨時轉換Web服務的例子,你可以在w3schools.com找到這個例子)。

private void DoGroupCall() 
    { 
     uint count = 5; 
     GroupAction action = new GroupAction(count, 
      () => 
      { 
       MessageBox.Show("Finished All Tasks"); 
      }); 

     for (uint i = 0; i < count; i++) 
     { 
      TempConvertHttpPost proxy = new TempConvertHttpPostClient(new BasicHttpBinding(), new EndpointAddress("http://localhost/webservices/tempconvert.asmx")); 
      CelsiusToFahrenheitRequest request = new CelsiusToFahrenheitRequest() { Celsius = "100" }; 

      proxy.BeginCelsiusToFahrenheit(request, 
        (ar) => Deployment.Current.Dispatcher.BeginInvoke(
        () => 
         { 
          CelsiusToFahrenheitResponse response = proxy.EndCelsiusToFahrenheit(ar); 

          // Other code presumably... 

          action.SingleActionComplete(); 
         } 
       ), null); 
     } 
    } 

注意GroupAction實例是如何用一個內聯回調委託定義,然後SingleCallBack函數每次調用的服務回報。

希望這有助於...

1

假設你使用Silverlight和你沒有獲得信號燈,你可能會尋找像這樣(寫在記事本,所以沒有承諾爲完美的語法):

int completedCallCount = 0; 
int targetCount = mAllPositions.Count; 

using (ManualResetEvent manualResetEvent = new ManualResetEvent(false)) 
{ 
    proxy.DoAsyncCallCompleted += (s, e) => 
    { 
     if (Interlocked.Increment(ref completedCallCount) == targetCount) 
     { 
      manualResetEvent.Set(); 
     } 
    }; 

    foreach (var position in mAllPositions) 
    { 
     proxy.DoAsyncCall(position); 
    } 

    // This will wait until all the events have completed. 
    manualResetEvent.WaitOne(); 
} 

然而,Silverlight的好處之一強迫你異步加載的數據是,你必須努力進行服務呼叫時鎖定UI起來,這正是你這個代碼就是這樣做的,除非你在後臺線程上運行它我強烈建議你這樣做。您可以隨時顯示「請稍候」消息,直到後臺進程完成。

+0

什麼是在異步調用中實現和使用「請稍候...」消息的好方法,以及如果有多個異步調用會怎麼樣。 – VoodooChild

+0

我已經爲Silverlight編寫了Semaphores:http://modosansreves-coding.blogspot.com/2011/08/semaphore-in-silverlight.html –