Poczekaj na void async method


155

Jak mogę czekać, aż void asyncmetoda zakończy swoją pracę?

na przykład mam funkcję jak poniżej:

async void LoadBlahBlah()
{
    await blah();
    ...
}

teraz chcę się upewnić, że wszystko zostało załadowane, zanim przejdę do innego miejsca.

Odpowiedzi:


234

Najlepszą praktyką jest zaznaczanie funkcji async voidtylko 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())


blah oczywiście zwraca zadanie i ma asynchroniczny podpis, dlaczego miałbyś to wywołać bez czekania. nawet VS da ci ostrzeżenie.
batmaci

8
await Task.Run(() => An_async_void_method_I_can_not_modify_now())
themefield

1
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.
Jonathan Lidbeck

@JonathanLidbeck stwierdzenie "kontynuowane jest natychmiast, zanim blah jest błędne. Spróbuj" await Task.Run (() => Thread.Sleep (10_000)) ", zadanie jest oczekiwane przez 10 sekund + przed wykonaniem następnej linii
Rohit Sharma

@RohitSharma, to jest poprawne dla twojego przykładu, ale Thread.Sleepnie jest asynchroniczne. To pytanie dotyczy oczekiwania na async voidfunkcję, powiedzmyasync void blah() { Task.Delay(10000); }
Jonathan Lidbeck


27

Najlepszym rozwiązaniem jest użycie async Task. Należy tego unikać async voidz 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ć SemaphoreSlimsygnału metody, gdy ma zamiar zakończyć. Rozważ zrobienie tego w finallybloku.


1

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


1

Naprawdę nie musisz nic robić ręcznie, awaitsł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();
}

Tjest typem blah()zwracanego obiektu

Nie można naprawdę funkcja więc może nie byćawaitvoidLoadBlahBlah()void


Chcę poczekać na LoadBlahBlah()zakończenie, a nieblah()
MBZ

10
Niestety asynchroniczny void metody nie wstrzymać wykonanie (w przeciwieństwie do asynchronicznej metody zadania)
ghord

-1

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.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.