W Visual Basic wywołania asynchroniczne i obsługa zdarzeń często realizowane są na różne sposoby, w zależności od wersji języka i typu aplikacji. W klasycznych aplikacjach WinForms VB.NET obsługa kolejki zdarzeń (event queue) zazwyczaj polega na wywołaniu Application.DoEvents(). Ta metoda pozwala obsłudze zdarzeń "zwolnić kontrolę", aby inne komunikaty mogły być obsługiwane, nie blokując głównego wątku:
While loading Application.DoEvents() 'Pozwala UI reagować na działania użytkownika End While
Do asynchronicznego wykonywania zadań w .NET z VB.NET używane są:
BackgroundWorker — komponent WinForms do przenoszenia długotrwałych operacji do osobnego wątku z bezpieczną interakcją z UI-wątkiem.Async/Await — wygodny nowoczesny system asynchroniczności, który pojawił się w .NET 4.5, pozwala pisać asynchroniczny kod "jak synchroniczny":Public Async Function LoadDataAsync() As Task Dim result As String = Await GetWebDataAsync() TextBox1.Text = result End Function
Różnice:
DoEvents po prostu przetwarza kolejkę komunikatów UI i nie tworzy nowych wątków.BackgroundWorker przenosi pracę do osobnego wątku, z wydarzeniem do bezpiecznego powrotu do UI-wątku.Async/Await realizuje asynchroniczność, jednocześnie enkapsulując zwrot kontroli do UI-wątku.W czym tkwi niebezpieczeństwo użycia
DoEventsw pętli, jeśli trzeba wczytać plik z dysku i aktualizować ProgressBar?
Odpowiedź:
DoEvents tymczasowo przekazuje kontrolę innym zdarzeniom Windows, ale nie zwalnia wątku. Jeśli używać go w ciężkich pętlach (np. wczytywanie dużego pliku z jednoczesnym aktualizowaniem interfejsu), mogą wystąpić nieoczekiwane błędy: wydarzenia użytkownika i zdarzenia myszy będą przetwarzane, co może prowadzić do ponownego uruchomienia przetwarzania lub nawet "zawieszenia" aplikacji. Jeśli zakłada się ciężkie ładowanie — należy użyć BackgroundWorker lub Task.
Przykład złego podejścia:
For i = 1 To 1000000 ProgressBar1.Value = i / 10000 Application.DoEvents() 'Nie zwalnia głównego wątku! Next
Historia
DoEvents wewnątrz długotrwałej pętli wczytywania pliku. W efekcie interfejs "zawieszał się": jeśli użytkownik kliknął przycisk "Otwórz" jeszcze raz — uruchamiała się druga operacja wczytywania pliku, mieszając się z pierwszą, co prowadziło do zniekształcenia danych i awarii aplikacji.Historia
DoEvents w pętli, ponieważ nie znał BackgroundWorker. Podczas aktualizacji ogromnego zbioru punktów aplikacja zaczęła zawieszać się i ostatecznie "wylatywała" z powodu przepełnienia kolejki komunikatów Windows.Historia
Używano asynchronicznego wywołania przez Async/Await, nie rozumiejąc, że długą pracę nie można uruchamiać w "gorącym" UI-wątku bez Await Task.Run(...). Rezultat: interfejs nie reagował na kliknięcia, a pasek postępu nie był aktualizowany, ponieważ długi metod wciąż wykonywał się na głównym wątku.