Chcę poczekać na zakończenie zadania <T> z pewnymi specjalnymi regułami: jeśli nie zakończyło się ono po X milisekundach, chcę wyświetlić wiadomość dla użytkownika. A jeśli nie zakończy się po upływie milisekund Y, chcę automatycznie poprosić o anulowanie .
Mogę użyć Task.ContinueWith, aby asynchronicznie czekać na zakończenie zadania (tj. Zaplanować wykonanie akcji po zakończeniu zadania), ale to nie pozwala określić limitu czasu. Mogę użyć Task.Wait, aby synchronicznie czekać na zakończenie zadania z limitem czasu, ale to blokuje mój wątek. Jak asynchronicznie czekać na zakończenie zadania z limitem czasu?
CancellationTokenSource
. Dostępne są dwa przeciążenia konstruktora, jedno z opóźnieniem całkowitym o milisekundę, a drugie z opóźnieniem TimeSpan.Odpowiedzi:
Co powiesz na to:
A oto świetny wpis na blogu „Crafting a Task.TimeoutAfter Method” (od zespołu MS Parallel Library) z dodatkowymi informacjami na ten temat .
Dodanie : na prośbę o komentarz do mojej odpowiedzi, oto rozszerzone rozwiązanie obejmujące obsługę anulowania. Pamiętaj, że przekazanie anulowania do zadania i timera oznacza, że w kodzie może wystąpić anulowanie na wiele sposobów, dlatego powinieneś sprawdzić i upewnić się, że poprawnie wykonałeś wszystkie z nich. Nie pozostawiaj przypadkowi różnych kombinacji i miej nadzieję, że komputer działa prawidłowo w czasie wykonywania.
źródło
Task.Delay
zadanie jest wspierane przez zegar systemowy, który będzie śledzony aż do upływu limitu czasu, niezależnie od tego, jak długoSomeOperationAsync
to potrwa. Więc jeśli ten ogólny fragment kodu wykonuje się często w ciasnej pętli, zużywasz zasoby systemowe dla liczników czasu, aż upłynie limit czasu. Sposobem na rozwiązanie tego problemu jestCancellationToken
przekazanie go doTask.Delay(timeout, cancellationToken)
anulowania poSomeOperationAsync
zakończeniu w celu zwolnienia zasobu timera.Task
, każdy wyjątek przechowywany przez zadanie zostanie w tym momencie ponownie zgłoszony. Daje to szansę na złapanieOperationCanceledException
(jeśli zostanie anulowane) lub inny wyjątek (jeśli nastąpi błąd).Task.Wait(timeout)
synchronicznie blokowałby zamiast asynchronicznie oczekiwać.Oto wersja metody rozszerzenia, która obejmuje anulowanie limitu czasu po zakończeniu pierwotnego zadania, zgodnie z sugestią Andrew Arnotta w komentarzu do jego odpowiedzi .
źródło
using
blokutask.Result
gdy zostanie wykonany dwukrotnie.task
) nadal będzie działać w przypadku przekroczenia limitu czasu?TimeoutException
ma odpowiedni domyślny komunikat. Przesłonięcie go przez „Upłynął limit czasu operacji”. nie dodaje żadnej wartości i faktycznie powoduje pewne zamieszanie, sugerując, że istnieje powód, aby ją zastąpić.Możesz użyć,
Task.WaitAny
aby poczekać na pierwsze z wielu zadań.Możesz utworzyć dwa dodatkowe zadania (które zostaną ukończone po upływie określonych limitów czasu), a następnie użyć,
WaitAny
aby poczekać na to, co zakończy się jako pierwsze. Jeśli zadanie, które zostało ukończone jako pierwsze, jest Twoim zadaniem „roboczym”, to gotowe. Jeśli zadanie, które zostało ukończone jako pierwsze, jest limitem czasu, możesz zareagować na limit czasu (np. Anulowanie żądania).źródło
below
... co to jest? na podstawie zamówienia SO?Co powiesz na coś takiego?
Możesz użyć opcji Task.Wait bez blokowania głównego wątku za pomocą innego zadania.
źródło
Oto w pełni działający przykład oparty na najczęściej głosowanej odpowiedzi, czyli:
Główną zaletą implementacji w tej odpowiedzi jest to, że dodano ogólne, więc funkcja (lub zadanie) może zwrócić wartość. Oznacza to, że każdą istniejącą funkcję można zawinąć w funkcję limitu czasu, np .:
Przed:
Po:
Ten kod wymaga .NET 4.5.
Ostrzeżenia
Po udzieleniu tej odpowiedzi generalnie nie jest dobrą praktyką zgłaszanie wyjątków w kodzie podczas normalnej pracy, chyba że absolutnie musisz:
Użyj tego kodu tylko wtedy, gdy absolutnie nie możesz zmienić funkcji, którą wywołujesz, więc upłynie limit czasu po określonym
TimeSpan
.Ta odpowiedź ma zastosowanie tylko w przypadku bibliotek bibliotek stron trzecich, których po prostu nie można refaktoryzować w celu włączenia parametru limitu czasu.
Jak napisać solidny kod
Jeśli chcesz napisać solidny kod, ogólna zasada jest następująca:
Jeśli nie zastosujesz się do tej reguły, Twój kod w końcu uderzy w operację, która z jakiegoś powodu zakończy się niepowodzeniem, a następnie zablokuje się na czas nieokreślony, a aplikacja właśnie zawiesiła się na stałe.
Jeśli po pewnym czasie nastąpił rozsądny limit czasu, aplikacja zawiesiłaby się na bardzo długi czas (np. 30 sekund), a następnie albo wyświetli błąd i będzie kontynuowała swoją wesołą drogę, albo spróbuje ponownie.
źródło
Korzystając z doskonałej biblioteki AsyncEx Stephena Cleary'ego , możesz:
TaskCanceledException
zostanie wyrzucony w przypadku przekroczenia limitu czasu.źródło
To jest nieco ulepszona wersja poprzednich odpowiedzi.
CancellationToken
oryginalne zadanie, a gdy nastąpi przekroczenie limitu czasu, otrzymaszTimeoutException
zamiastOperationCanceledException
.Stosowanie
InnerCallAsync
jego ukończenie może zająć dużo czasu.CallAsync
owija to limitem czasu.źródło
timeoutCancellation
dodelayTask
. Obecnie, jeśli anulujesz ostrzał,CancelAfterAsync
możesz rzucićTimeoutException
zamiastTaskCanceledException
, bo przyczynądelayTask
może być pierwsza.WhenAny
zostaną anulowane tym samym tokenem,WhenAny
zwróci pierwsze zadanie. To założenie było błędne. Zredagowałem odpowiedź. Dzięki!Użyj timera, aby obsłużyć wiadomość i automatyczne anulowanie. Po zakończeniu zadania zadzwoń po zegar na Dispose, aby nigdy nie strzelały. Oto przykład; zmień zadanie Opóźnij do 500, 1500 lub 2500, aby zobaczyć różne przypadki:
Ponadto Async CTP udostępnia metodę TaskEx.Delay, która zawija timery w zadaniach dla Ciebie. Może to dać ci większą kontrolę nad takimi czynnościami, jak ustawianie TaskScheduler dla kontynuacji po uruchomieniu Timera.
źródło
task.Wait()
.Innym sposobem rozwiązania tego problemu jest użycie rozszerzeń reaktywnych:
Przetestuj powyżej, używając poniższego kodu w teście jednostkowym, działa dla mnie
Może być potrzebny następujący obszar nazw:
źródło
Ogólna wersja powyższej odpowiedzi @ Kevana, wykorzystująca rozszerzenia reaktywne.
Z opcjonalnym harmonogramem:
BTW: Kiedy dojdzie do przekroczenia limitu czasu, zostanie zgłoszony wyjątek przekroczenia limitu czasu
źródło
Jeśli używasz BlockingCollection do zaplanowania zadania, producent może uruchomić potencjalnie długo działające zadanie, a konsument może skorzystać z metody TryTake, która ma wbudowany token limitu czasu i anulowania.
źródło
Czułem to
Task.Delay()
zadanie iCancellationTokenSource
w innych odpowiedziach trochę za mój przypadek użycia w ciasnej pętli sieciowej.I choć Joe Hoag's Crafting a Task.Timeout Po tym, jak metoda na blogach MSDN była inspirująca, byłem trochę zmęczony używaniem
TimeoutException
kontroli przepływu z tego samego powodu, co powyżej, ponieważ przekroczenia limitu czasu są oczekiwane częściej niż nie.Więc poszedłem z tym, który obsługuje również optymalizacje wspomniane na blogu:
Przykładowy przypadek użycia jest taki:
źródło
Kilka wariantów odpowiedzi Andrew Arnotta:
Jeśli chcesz poczekać na istniejące zadanie i dowiedzieć się, czy zostało ono ukończone, czy upłynął limit czasu, ale nie chcesz go anulować, jeśli upłynie limit czasu:
Jeśli chcesz rozpocząć zadanie robocze i anulować je, jeśli upłynie limit czasu:
Jeśli masz już utworzone zadanie, które chcesz anulować w przypadku przekroczenia limitu czasu:
Kolejny komentarz, te wersje anulują licznik czasu, jeśli limit czasu nie wystąpi, więc wiele wywołań nie spowoduje gromadzenia się liczników.
sjb
źródło
Ponownie łączę pomysły innych odpowiedzi tutaj i tej odpowiedzi w innym wątku w metodę rozszerzenia typu Try. Jest to korzystne, jeśli chcesz zastosować metodę rozszerzenia, ale unikając wyjątku po przekroczeniu limitu czasu.
źródło
Zdecydowanie nie rób tego, ale jest to opcja, jeśli ... Nie mogę wymyślić ważnego powodu.
źródło