Odpowiedzi:
Najlepszą praktyką jest zaznaczanie funkcji async void
tylko wtedy, gdy jest to metoda odpal i zapomnij, jeśli chcesz poczekać, powinieneś oznaczyć ją jako async Task
.
Jeśli nadal chcesz poczekać, zawiń to w ten sposób await Task.Run(() => blah())
await Task.Run(() => An_async_void_method_I_can_not_modify_now())
await Task.Run(() => blah())
wprowadza w błąd. To nie czeka na zakończenie funkcji asynchronicznej blah
, tylko czeka na (trywialne) utworzenie zadania i jest kontynuowane bezpośrednio przed blah()
zakończeniem.
Thread.Sleep
nie jest asynchroniczne. To pytanie dotyczy oczekiwania na async void
funkcję, powiedzmyasync void blah() { Task.Delay(10000); }
Jeśli możesz zmienić podpis swojej funkcji na async Task
, możesz użyć przedstawionego tutaj kodu
Najlepszym rozwiązaniem jest użycie async Task
. Należy tego unikać async void
z kilku powodów, z których jednym jest składalność.
Jeśli metoda nie może powrócić Task
(np. Jest to program obsługi zdarzenia), możesz użyć SemaphoreSlim
sygnału metody, gdy ma zamiar zakończyć. Rozważ zrobienie tego w finally
bloku.
wykonaj AutoResetEvent, wywołaj funkcję, a następnie poczekaj na AutoResetEvent, a następnie ustaw ją w async void, gdy wiesz, że jest wykonana.
Możesz także poczekać na zadanie, które wróci z Twojej pustej asynchronicznej
Naprawdę nie musisz nic robić ręcznie, await
słowo kluczowe wstrzymuje wykonywanie funkcji do czasu blah()
powrotu.
private async void SomeFunction()
{
var x = await LoadBlahBlah(); <- Function is not paused
//rest of the code get's executed even if LoadBlahBlah() is still executing
}
private async Task<T> LoadBlahBlah()
{
await DoStuff(); <- function is paused
await DoMoreStuff();
}
T
jest typem blah()
zwracanego obiektu
Nie można naprawdę funkcja więc może nie byćawait
void
LoadBlahBlah()
void
LoadBlahBlah()
zakończenie, a nieblah()
Wiem, że to stare pytanie, ale wciąż jest to problem, do którego wciąż wkraczam, a mimo to nadal nie ma jasnego rozwiązania, aby zrobić to poprawnie, używając async / await w metodzie async void signature.
Jednak zauważyłem, że .Wait () działa poprawnie wewnątrz metody void.
a ponieważ async void i void mają ten sam podpis, może być konieczne wykonanie następujących czynności.
void LoadBlahBlah()
{
blah().Wait(); //this blocks
}
Dość myląco async / await nie blokuje następnego kodu.
async void LoadBlahBlah()
{
await blah(); //this does not block
}
Kiedy dekompilujesz swój kod, przypuszczam, że async void tworzy wewnętrzne zadanie (podobnie jak async Task), ale ponieważ podpis nie obsługuje zwracania tych wewnętrznych zadań
oznacza to, że wewnętrznie metoda async void nadal będzie mogła „oczekiwać” wewnętrznie asynchronicznych metod. ale zewnętrznie nie jest w stanie wiedzieć, kiedy zadanie wewnętrzne jest zakończone.
Mój wniosek jest taki, że async void działa zgodnie z przeznaczeniem, a jeśli potrzebujesz informacji zwrotnej z wewnętrznego zadania, musisz zamiast tego użyć podpisu async Task.
mam nadzieję, że moje wędrówki mają sens dla każdego, kto szuka odpowiedzi.
Edycja: stworzyłem przykładowy kod i zdekompilowałem go, aby zobaczyć, co się właściwie dzieje.
static async void Test()
{
await Task.Delay(5000);
}
static async Task TestAsync()
{
await Task.Delay(5000);
}
Zamienia się w (edytuj: wiem, że kod ciała nie jest tutaj, ale w statemachines, ale statemachines był w zasadzie identyczny, więc nie zawracałem sobie głowy dodawaniem ich)
private static void Test()
{
<Test>d__1 stateMachine = new <Test>d__1();
stateMachine.<>t__builder = AsyncVoidMethodBuilder.Create();
stateMachine.<>1__state = -1;
AsyncVoidMethodBuilder <>t__builder = stateMachine.<>t__builder;
<>t__builder.Start(ref stateMachine);
}
private static Task TestAsync()
{
<TestAsync>d__2 stateMachine = new <TestAsync>d__2();
stateMachine.<>t__builder = AsyncTaskMethodBuilder.Create();
stateMachine.<>1__state = -1;
AsyncTaskMethodBuilder <>t__builder = stateMachine.<>t__builder;
<>t__builder.Start(ref stateMachine);
return stateMachine.<>t__builder.Task;
}
ani AsyncVoidMethodBuilder, ani AsyncTaskMethodBuilder w rzeczywistości nie mają żadnego kodu w metodzie Start, który wskazywałby na ich blokowanie i zawsze byłyby uruchamiane asynchronicznie po ich uruchomieniu.
co oznacza, że bez zwracającego zadania nie byłoby możliwości sprawdzenia, czy zostało ono zakończone.
zgodnie z oczekiwaniami uruchamia tylko zadanie działające asynchronicznie, a następnie kontynuuje je w kodzie. i zadanie asynchroniczne, najpierw uruchamia zadanie, a następnie zwraca je.
więc myślę, że moją odpowiedzią byłoby nigdy nie używać async void, jeśli chcesz wiedzieć, kiedy zadanie jest wykonane, do tego służy Async Task.