Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Implementacja wzorca asynchronicznego opartego na zdarzeniach
Artykuł
08.04.2023
Jeśli piszesz klasę z niektórymi operacjami, które mogą powodować zauważalne opóźnienia, rozważ nadanie jej funkcji asynchronicznej przez zaimplementowanie asynchronicznego wzorca opartego na zdarzeniach.
Wzorzec asynchroniczny oparty na zdarzeniach zapewnia ustandaryzowany sposób tworzenia pakietu klasy, która ma funkcje asynchroniczne. Jeśli zaimplementowano przy użyciu klas pomocnika, takich jak AsyncOperationManager, klasa będzie działać poprawnie w dowolnym modelu aplikacji, w tym w ASP.NET, aplikacjach konsolowych i aplikacjach Windows Forms.
Możliwości implementacji wzorca asynchronicznego opartego na zdarzeniach
Rozważ zaimplementowanie wzorca asynchronicznego opartego na zdarzeniach, gdy:
Klienci klasy nie potrzebują WaitHandle obiektów i IAsyncResult są dostępne dla operacji asynchronicznych, co oznacza, że sondowanie i WaitAll lub WaitAny konieczne będzie skompilowanie przez klienta.
Chcesz, aby operacje asynchroniczne były zarządzane przez klienta za pomocą znanego modelu zdarzenia/delegata.
Każda operacja jest kandydatem do implementacji asynchronicznej, ale należy wziąć pod uwagę te, które powinny powodować długie opóźnienia. Szczególnie odpowiednie są operacje, w których klienci nazywają metodę i są powiadamiani po zakończeniu, bez konieczności dalszej interwencji. Odpowiednie są również operacje, które są uruchamiane w sposób ciągły, okresowo powiadamiając klientów o postępie, wyniki przyrostowe lub zmiany stanu.
Aby uzyskać więcej informacji na temat określania, kiedy należy obsługiwać wzorzec asynchroniczny oparty na zdarzeniach, zobacz Wybieranie, kiedy należy zaimplementować wzorzec asynchroniczny oparty na zdarzeniach.
Nazewnictwo metod asynchronicznych
Dla każdej synchronicznej metody MethodName , dla której chcesz podać asynchroniczny odpowiednik:
Zdefiniuj metodę MethodNameAsync , która:
Zwraca wartość void.
Przyjmuje te same parametry co metoda MethodName .
Akceptuje wiele wywołań.
Opcjonalnie zdefiniuj przeciążenie MethodName Async, identyczne z metodą MethodNameAsync, ale z dodatkowym parametrem o nazwie userState. Zrób to, jeśli chcesz zarządzać wieloma współbieżnych wywołań metody, w takim przypadku userState wartość zostanie dostarczona z powrotem do wszystkich procedur obsługi zdarzeń w celu odróżnienia wywołań metody. Możesz również to zrobić po prostu jako miejsce do przechowywania stanu użytkownika na potrzeby późniejszego pobierania.
Dla każdej oddzielnej sygnatury metody MethodNameAsync :
Zdefiniuj następujące zdarzenie w tej samej klasie co metoda:
Public Event MethodNameCompleted As MethodNameCompletedEventHandler
public event MethodNameCompletedEventHandler MethodNameCompleted;
Zdefiniuj następujący delegat i AsyncCompletedEventArgs. Prawdopodobnie zostaną one zdefiniowane poza samą klasą, ale w tej samej przestrzeni nazw.
Public Delegate Sub MethodNameCompletedEventHandler( _
ByVal sender As Object, _
ByVal e As MethodNameCompletedEventArgs)
Public Class MethodNameCompletedEventArgs
Inherits System.ComponentModel.AsyncCompletedEventArgs
Public ReadOnly Property Result() As MyReturnType
End Property
public delegate void MethodNameCompletedEventHandler(object sender,
MethodNameCompletedEventArgs e);
public class MethodNameCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs
{
public MyReturnType Result { get; }
}
Upewnij się, że klasa MethodNameCompletedEventArgs uwidacznia jego składowe jako właściwości tylko do odczytu, a nie pola, ponieważ pola uniemożliwiają powiązanie danych.
Jest to całkowicie akceptowalne, jeśli jest to możliwe i odpowiednie, aby ponownie używać delegatów i AsyncCompletedEventArgs typów. W takim przypadku nazewnictwo nie będzie zgodne z nazwą metody, ponieważ dany delegat i AsyncCompletedEventArgs nie będzie powiązany z jedną metodą.
Opcjonalnie obsługa anulowania
Jeśli klasa będzie obsługiwać anulowanie operacji asynchronicznych, anulowanie powinno zostać ujawnione klientowi zgodnie z poniższym opisem. Przed zdefiniowaniem pomocy technicznej anulowania należy uzyskać dwa punkty decyzyjne:
Czy klasa, w tym przyszłe przewidywane dodatki, ma tylko jedną operację asynchroniczną, która obsługuje anulowanie?
Czy operacje asynchroniczne, które obsługują anulowanie, obsługują wiele oczekujących operacji? Oznacza to, czy metoda MethodNameAsync bierze userState parametr i czy zezwala na wiele wywołań przed oczekiwaniem na zakończenie?
Skorzystaj z odpowiedzi na te dwa pytania w poniższej tabeli, aby określić, jaki powinien być podpis metody anulowania.
Visual Basic
Obsługa wielu równoczesnych operacji
Tylko jedna operacja w danym momencie
Jedna operacja asynchronizuj w całej klasie
Sub MethodNameAsyncCancel(ByVal userState As Object)
Sub MethodNameAsyncCancel()
Wiele operacji asynchronicznych w klasie
Sub CancelAsync(ByVal userState As Object)
Sub CancelAsync()
C#
Obsługa wielu równoczesnych operacji
Tylko jedna operacja w danym momencie
Jedna operacja asynchronizuj w całej klasie
void MethodNameAsyncCancel(object userState);
void MethodNameAsyncCancel();
Wiele operacji asynchronicznych w klasie
void CancelAsync(object userState);
void CancelAsync();
Jeśli zdefiniujesz metodę CancelAsync(object userState) , klienci muszą zachować ostrożność podczas wybierania ich wartości stanu, aby umożliwić im rozróżnienie między wszystkimi metodami asynchronicznymi wywoływanymi w obiekcie, a nie tylko między wszystkimi wywołaniami pojedynczej metody asynchronicznej.
Decyzja o nazwie methodNameAsyncCancel o jedną operację asynchronicznego polega na tym, że można łatwiej odnaleźć metodę w środowisku projektowym, na przykład IntelliSense programu Visual Studio. Spowoduje to zgrupowanie powiązanych członków i odróżnienie ich od innych elementów członkowskich, które nie mają nic wspólnego z funkcją asynchroniczną. Jeśli spodziewasz się, że w kolejnych wersjach mogą istnieć dodatkowe operacje asynchroniczne, lepiej jest zdefiniować CancelAsyncelement .
Nie należy definiować wielu metod z powyższej tabeli w tej samej klasie. To nie będzie miało sensu lub będzie zaśmiecać interfejs klasy z proliferacji metod.
Te metody zazwyczaj będą zwracane natychmiast, a operacja może lub nie może zostać anulowana. W procedurze obsługi zdarzeń dla zdarzenia MethodName Completed obiekt MethodNameCompletedEventArgs zawiera Cancelled pole, którego klienci mogą użyć do określenia, czy nastąpiło anulowanie.
Jeśli klasa nie obsługuje wielu współbieżnych wywołań, rozważ ujawnienie IsBusy właściwości. Dzięki temu deweloperzy mogą określić, czy metoda MethodNameAsync jest uruchomiona bez przechwytywania wyjątku od metody MethodNameAsync.
Często pożądane jest, aby operacja asynchroniczna zgłaszała postęp podczas jego operacji. Wzorzec asynchroniczny oparty na zdarzeniach zawiera wskazówki dotyczące tego działania.
Opcjonalnie zdefiniuj zdarzenie, które ma być wywoływane przez operację asynchroniczną i wywoływane w odpowiednim wątku. Obiekt ProgressChangedEventArgs zawiera wskaźnik postępu o wartości całkowitej, który powinien należeć do przedziału od 0 do 100.
Nadaj temu zdarzeniu nazwę w następujący sposób:
ProgressChanged jeśli klasa ma wiele operacji asynchronicznych (lub oczekuje się, że wzrośnie, aby uwzględnić wiele operacji asynchronicznych w przyszłych wersjach);
MethodNameProgressChanged , jeśli klasa ma jedną operację asynchroniczną.
Ten wybór nazewnictwa jest równoległy, który został wykonany dla metody anulowania, zgodnie z opisem w sekcji Opcjonalne anulowanie obsługi.
To zdarzenie powinno używać sygnatury delegata ProgressChangedEventHandler i ProgressChangedEventArgs klasy . Alternatywnie, jeśli można podać bardziej specyficzny dla domeny wskaźnik postępu (na przykład bajty odczytu i sumy bajtów dla operacji pobierania), należy zdefiniować klasę ProgressChangedEventArgspochodną klasy .
Należy pamiętać, że dla klasy istnieje tylko jedno ProgressChanged zdarzenie Lub MethodNameProgressChanged , niezależnie od liczby obsługiwanych przez nią metod asynchronicznych. Oczekuje się, że klienci będą używać obiektu przekazanego userStatedo metod MethodNameAsync w celu odróżnienia między aktualizacjami postępu na wielu współbieżnych operacjach.
Mogą wystąpić sytuacje, w których wiele operacji obsługuje postęp, a każdy zwraca inny wskaźnik postępu. W takim przypadku pojedyncze ProgressChanged zdarzenie nie jest odpowiednie i można rozważyć obsługę wielu ProgressChanged zdarzeń. W tym przypadku należy użyć wzorca nazewnictwa metody MethodNameProgressChanged dla każdej metody MethodNameAsync.
Czasami operacja asynchroniczna może zwracać wyniki przyrostowe przed zakończeniem. Istnieje wiele opcji, których można użyć do obsługi tego scenariusza. Poniżej przedstawiono kilka przykładów.
Klasa pojedynczej operacji
Jeśli klasa obsługuje tylko jedną operację asynchroniczną, a ta operacja może zwrócić wyniki przyrostowe, wówczas:
ProgressChangedEventArgs Rozszerz typ tak, aby przenosił dane wynikowe przyrostowe, i zdefiniuj zdarzenie MethodNameProgressChanged przy użyciu tych rozszerzonych danych.
Zgłoś to zdarzenie MethodNameProgressChanged , gdy istnieje wynik przyrostowy do raportowania.
To rozwiązanie ma zastosowanie w szczególności do pojedynczej klasy operacji asynchronicznej, ponieważ nie ma problemu z tym samym zdarzeniem, które występuje w celu zwrócenia wyników przyrostowych dla "wszystkich operacji", jak to robi zdarzenie MethodNameProgressChanged .
Klasa wielokrotnej operacji z homogenicznymi wynikami przyrostowymi
W takim przypadku klasa obsługuje wiele metod asynchronicznych, z których każda może zwracać wyniki przyrostowe, a wszystkie te przyrostowe wyniki mają ten sam typ danych.
Postępuj zgodnie z modelem opisanym powyżej dla klas pojedynczej operacji, ponieważ ta sama EventArgs struktura będzie działać dla wszystkich wyników przyrostowych. Zdefiniuj ProgressChanged zdarzenie zamiast zdarzenia MethodNameProgressChanged , ponieważ dotyczy wielu metod asynchronicznych.
Klasa wielokrotnej operacji z heterogenicznymi wynikami przyrostowymi
Jeśli klasa obsługuje wiele metod asynchronicznych, każdy zwraca inny typ danych, należy wykonać następujące czynności:
Oddziel raportowanie wyników przyrostowych od raportowania postępu.
Zdefiniuj oddzielne zdarzenie MethodNameProgressChanged odpowiednie EventArgs dla każdej metody asynchronicznej w celu obsługi przyrostowych danych wynikowych tej metody.
Mimo że korzystanie z platformy out i ref jest ogólnie zniechęcane do korzystania z platformy .NET, poniżej przedstawiono reguły, które należy przestrzegać, gdy są obecne:
Biorąc pod uwagę synchroniczną metodę MethodName:
outparametry methodName nie powinny być częścią metody MethodNameAsync. Zamiast tego powinny one być częścią MethodNameCompletedEventArgs o takiej samej nazwie jak jej odpowiednik parametru w MethodName (chyba że istnieje bardziej odpowiednia nazwa).
refparametry metody MethodName powinny być wyświetlane jako część metody MethodName Async, a w ramach metody MethodNameCompletedEventArgs o takiej samej nazwie jak jej odpowiednik parametru w MethodName (chyba że istnieje bardziej odpowiednia nazwa).
Na przykład podane:
Public Function MethodName(ByVal arg1 As String, ByRef arg2 As String, ByRef arg3 As String) As Integer
public int MethodName(string arg1, ref string arg2, out string arg3);
Public Sub MethodNameAsync(ByVal arg1 As String, ByVal arg2 As String)
Public Class MethodNameCompletedEventArgs
Inherits System.ComponentModel.AsyncCompletedEventArgs
Public ReadOnly Property Result() As Integer
End Property
Public ReadOnly Property Arg2() As String
End Property
Public ReadOnly Property Arg3() As String
End Property
End Class
public void MethodNameAsync(string arg1, string arg2);
public class MethodNameCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs
{
public int Result { get; };
public string Arg2 { get; };
public string Arg3 { get; };
}
Źródło tej zawartości można znaleźć w witrynie GitHub, gdzie można również tworzyć i przeglądać problemy i żądania ściągnięcia. Więcej informacji znajdziesz w naszym przewodniku dla współtwórców.
Opinia o produkcie
.NET
.NET
to projekt typu open source. Wybierz link, aby przekazać opinię:
Przejrzyj wzorce asynchroniczne oparte na zdarzeniach (EAP) na platformie .NET, które udostępniają zalety wielowątków aplikacji, ale ukrywają pewne złożoności projektowe.
Dowiedz się więcej o: How to: Implement a Client of the Event-based Asynchronous Pattern (Instrukcje: implementowanie klienta wzorca asynchronicznego opartego na zdarzeniach)
Zobacz linki do artykułów dotyczących asynchronicznego wzorca opartego na zdarzeniach (EAP) na platformie .NET, takich jak implementacja, najlepsze rozwiązania, implementowanie klienta protokołu EAP i nie tylko.