2010-02-21 14 views
6

Mam kod jak poniżej:Silverlight, do czynienia z Async wywołuje

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

Więc jak mogę zrobić powyższych?
mógłby zrobić coś takiego:

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

i liczyć przyrost po każdej rozmowie Async jest ... ale to nie wydaje się dobrym sposobem na to zrobić

jakąś radę? Czy istnieje jakiś wzorzec projektowy dla powyższego problemu, ponieważ jestem pewien, że jest to typowy scenariusz?

Odpowiedz

0

(NB, daję wam dwa oddzielne odpowiedzi. To jeden kije tak blisko do pytania, jak to możliwe.)

Twoje pytanie mówi

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

ale zgaduję, że to, co naprawdę znaczy jest:

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

??

Jeśli tak, to można bardziej skutecznie osiągnąć to samo za pomocą semafora:

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

Jak każde wywołanie asynchroniczne zakończeniu zwolnić semafora.

TheSemaphore.Release(); 

Przed wykonaniem ostatecznego kodu, upewnij się, że semafor został wydany wymaganą liczbę razy:

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

Twój kod będzie blokować aż operacje asynchroniczne wszystkie zostały zakończone.

+0

Semafor na Silverlight? –

0

Twoje pytanie jest trochę niejasny (powinno być count <=?), Ale tu idzie ...

co prosicie o to jak zrobić synchronicznego połączenia. W przypadku połączenia asynchronicznego, zanim wykonasz wywołanie, przypiszesz funkcję obsługi zdarzenia, która zostanie wywołana po zakończeniu połączenia, a następnie wykonasz połączenie. Oznacza to, że kod zakończenia jest w innej funkcji niż kod wywołujący. Oznacza to, że jeśli twoje połączenie asynchroniczne zostanie wykonane w wątku UI, interfejs użytkownika nie będzie blokował się podczas wykonywania połączenia.

Połączenia synchronizacyjne są możliwe w Silverlight, ale musisz upewnić się, że nie robisz ich w wątku UI. Jednym ze sposobów osiągnięcia tego jest rozpoczęcie nowego wątku tła, wykonanie asynchronicznego wywołania w wątku tła, ale zablokowanie powrotu, dopóki połączenie nie zostanie zakończone. Oto przykładowy kod pseudo:

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(); 
    } 
} 

Zauważ, że ten kod nie jest szczególnie wątku bezpieczne lub produkcja gotowy - to po prostu szybki próbki.

To, co się dzieje, polega na tym, że po wpisaniu MyCallFunction sprawdza, czy działa w wątku interfejsu użytkownika, jeśli jest, to ponownie wywołuje się w wątku tła. Następnie ustawia AutoResetEvent i wykonuje wywołanie asynchroniczne.Następnie zatrzymuje się na obiekcie myResetEvent, dopóki nie zostanie ustawiony (lub "zasygnalizowany") z obsługi zakończonej wywołania, przy której wykonanie kodu punktu będzie kontynuowane. Zauważ, że nie powinieneś próbować uzyskać dostępu do formantu bezpośrednio z tego kodu bez uprzedniego upewnienia się, że jesteś ponownie w wątku UI.

Kiedy zacząłem pracować, jak to zrobić w Silverlight, zacząłem od this SO post, i kontynuowałem z this link. Ale jak mówi Marc Gravell w pierwszym poście, nie rób tego, jeśli możesz tego uniknąć. Jedyne, co musiałem zrobić, to gdy musiałem zebrać wyniki kilku różnych wywołań WCF do jednego wyniku, który został zwrócony do interfejsu użytkownika (i te połączenia nie mogły być połączone z jedną metodą WCF w stylu fasady).

0

(NB, daję dwa oddzielne odpowiedzi; ma to zupełnie inne podejście niż w pytaniu.)

Następujące próbki kodu Miguel Madero w this URL, użyj reaktywnych Framework czekać równolegle dla wszystkich wyniki należy wypełnić, a następnie wykonać kolejne zadanie.

(Jest inna dyskusja w tym samym łańcuchu e-mail, w tym link do this closely related StackOverflow question.)

0
  1. konfiguracja statyczna autoResetEvent
  2. konfiguracji ThreadPoolQueueUserWorkItems
  3. ForEach pozycji w pętli, w kolejce do elementu, i przechodzą w autoResetEvent jako parametr wywołania zwrotnego jest.
  4. W ramach wywołania zwrotnego wykonaj zadanie, a następnie wywołaj metodę autoresetevent.Set();
  5. W mainbody w punkcie //I want to execute code here after each Async call has finished wywołać odpowiednią autoResetEvent.Wait();
0

Inną opcją jest zwiększenie licznika w pętli foreach, która wywołuje procesy asynchroniczne. Następnie w sprawdzaniu oddzwaniania zmniejszysz licznik i sprawdzisz równość do zera. Kiedy licznik powrócił do zera, wszystkie połączenia są zakończone i można uruchomić następny kod. Tylko pamiętaj, aby zmienić licznik w sposób bezpieczny dla wątków. Najprostszym sposobem na to może być powrót do wątku interfejsu użytkownika przed zmniejszeniem i przetestowaniem licznika.

0

nie widzę tego jako „chcę zrobić synchronicznego połączenia” problem. Wszystko, co naprawdę robisz, to wiązanie serii asynchronicznych połączeń, gdy wszystkie są kompletne.

Napotkałem to wcześniej w przypadku, gdy masz listę przedmiotów i musisz asynchronicznie pobierać dla nich atrybuty z serwera (niektóre dla statusu dla instancji), ale także chcesz zaktualizować wskaźnik postępu ładowania na ekranie .

W tej sytuacji nie chcesz, aby zablokować wątku UI, gdy wszystkie elementy są aktualizowane (blokowanie wątków jest złe w każdym razie jeśli istnieje alternatywa).

Więc użyłem klasy sterowania tak:

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(); 
      } 
     } 
    } 
} 

utworzyć tę akcję z mnożnikiem (dla liczby elementów) i wywołania zwrotnego, który zostanie wykonany po zakończeniu wszystkich przedmiotów.Zasadniczo jak semafor z większą kontrolą i bez obawy o nic.

Kod wywołujący wyglądałby tak (oparłem przykład na wywołaniu standardowej usługi internetowej do konwersji temp, którą można znaleźć na stronie 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); 
     } 
    } 

Uwaga jak instancja GroupAction jest zdefiniowana z delegatem inline zwrotnej, a następnie funkcja SingleCallBack jest wywoływany za każdym razem powraca serwisowych.

Nadzieja to pomaga ...

1

Zważywszy, że używasz Silverlight, i nie masz dostępu do semaforów, może być patrząc na coś takiego (napisany w notatniku, więc nie ma co do obietnic idealny składnia):

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(); 
} 

jednak, jedna z zalet Silverlight zmuszając do asynchronicznego ładowania danych jest to, że trzeba ciężko pracować, aby zablokować UI się przy podejmowaniu do zgłoszeń serwisowych, która jest dokładnie to, czego Zrobisz tak z kodem, chyba że masz go uruchomionego na wątku tła, wh ich gorąco polecam. Zawsze możesz wyświetlić komunikat "proszę czekać", aż proces w tle zakończy się.

+0

Jaki jest dobry sposób na wdrożenie i używanie komunikatów "Proszę czekać ..." w połączeniach asynchronicznych, a co jeśli jest wiele połączeń asynchronicznych. – VoodooChild

+0

Napisałem Semafory dla Silverlight: http://modosansreves-coding.blogspot.com/2011/08/semaphore-in-silverlight.html –

Powiązane problemy