2012-05-24 15 views
10

Po tym, jak moja aplikacja przestała działać, wyśledziłem przyczynę wątku oczekującego na zadanie utworzone przez Task.Delay() (lub TaskEx.Delay() w .NET 4.0), dla którego podano obliczoną wartość TimeSpan, która z powodu błędu była czasami obliczana na TimeSpan z wartością TotalMilliseconds mniejszą niż lub równą -1 i większą niż -2 (tj. od -10000 do -19999 znaczników włącznie).Dlaczego funkcja Task.Delay() zezwala na nieskończone opóźnienie?

Wydaje się, że kiedy przechodzą negatywny TimeSpan czyli -2 milisekund lub niższy, sposób prawidłowo zgłasza ArgumentOutOfRangeException, ale gdy podasz negatywny Okres z zakresu opisanego powyżej, zwraca Task że nigdy nie kończy (poprzez ustawienie podstawowy System.Threading.Timer do dueTime z -1, który oznacza nieskończoność). Oznacza to, że wszelkie kontynuacje ustawione dla tego zadania nigdy nie zostaną wykonane, a każdy ubogi wątek, który stanie się .Wait() na tym Task, zostanie na zawsze zablokowany.

Jakie możliwe zastosowanie może mieć Task, które nigdy nie zostanie ukończone? Czy ktokolwiek oczekiwałby takiej wartości zwrotu? Czy nie powinna być przekazywana żadna wartość ujemna do .Delay(), w tym wartości w tym zakresie specjalnym, wyrzucić ArgumentOutOfRangeException?

+1

Dokument MSDN jest dość jednoznaczny w dopuszczeniu wartości -1, więc wygląda na to, że zachowuje się poprawnie. Nie jestem pewien przypadku użycia dla tego przeciążenia, ale może to być sposób oczekiwania na "sprawiedliwe" anulowanie z przeciążeniem, które pobiera token anulowania. –

+0

@James: Nie jest jednoznaczne zezwalanie na -1, oznacza to niedozwolone wartości mniejsze niż -1. Nie mówi nawet, co się stanie, jeśli zdasz -1, w przeciwieństwie do dokumentacji "System.Threading.Timer". Wygląda na to, że udokumentowana lista wyjątków została automatycznie wygenerowana z kodu źródłowego. A jeśli czekasz na "sprawiedliwe" anulowanie, to dlaczego chcesz wykonać połączenie z 'Task.Delay()'? –

+0

, jeśli uważasz, że jest zepsuty, zgłoś błąd na łączu. Dokument, który mówi "niższy niż -1 jest nieprawidłowy" jest wyraźny (dla mnie) mówiąc, że -1 jest prawidłowe. Jeśli intencją było -1 jako nieważne "mniej niż 0 jest nieprawidłowe" byłoby łatwiejsze do napisania. Ponieważ zarówno dokument, jak i kod pozwalają na -1, myślę, że jest to według projektu, ale możesz wysłać plik o błędzie na połączenie (prawdopodobnie będzie to przetwarzane przez zespół BCL niż losowy wątek SO, tak mi się wydaje :) –

Odpowiedz

7

lub -1 jest przydatna, gdy chcesz czekać bezterminowo na długo trwające zadanie, które zajmie nieokreśloną ilość czasu, aby zakończyć, ale w końcu zakończyć.

Interfejs API Win32 wykorzystuje stałą INFINITE = -1 dla nieskończonych limitów czasu.

Zwykle nie chcesz używać go w wątku interfejsu użytkownika, ponieważ może to zablokować interfejs użytkownika (co wydaje się być twoim problemem). Ale istnieją wątki użycia w wątku roboczym - np. serwer, który blokuje oczekiwanie na połączenie od klienta.

+0

Przydatny jest nieskończony limit czasu. Nieskończone opóźnienie nie jest (i dlatego pierwszy akapit nie ma zastosowania). Co na świecie chcesz odłożyć na zawsze? Równie dobrze możesz tego nie robić. Po prostu nie ma dla mnie sensu, że szczegóły implementacji '-1' będą propagowane z' System.Threading.Timer' (lub timera Win32) aż do metody 'Task.Delay()'.Jest to sprzeczne z zasadą projektowania Microsoftu polegającą na popychaniu programistów do "dołu sukcesu", chyba że jest jakaś przypadek użycia, którego nie znam. –

+0

Ponadto nie używam go w wątku interfejsu użytkownika. Jest to usługa systemu Windows, która na żądanie zatrzymania musi wykonać procedurę zamykania, wywołując metodę opartą na [TAP] (http://www.microsoft.com/en-us/download/details.aspx?id=19957) może to potrwać zbyt długo, aby utworzyć zadanie opóźnienia, które wykona wymuszone wyłączenie. Następnie wykonuje polecenie '.WaitAny()' w obu zadaniach, a ponieważ pierwotne zadanie trwało bardzo długo, a zadanie opóźnienia nigdy się nie zakończy (zamiast rzucać wyjątek), usługa zawiesiła się. –

+1

W jaki sposób serwer, który blokuje oczekiwanie na połączenie z klientem, użyje "zadania", które nigdy się nie zakończy? Czy możesz pokazać przykład? –

2

W szyderczych scenariuszach, gdzie chcę się upewnić, że mój kod w bloku Task.WhenAny() obsługuje jedno z zadań oczekiwanych poprawnie, mogę wykpić inne zadania i użyć nieskończonego opóźnienia, aby upewnić się, że zadanie .Kiedy przetwarza to zadanie, nie kpiłem z nieskończonego opóźnienia.

+2

Interesujący przypadek użycia, chociaż myślę, że byłoby bardziej jednoznaczne i jasne użycie "nowego TaskCompletionSource()." Zadanie ", aby zapewnić niekończące się zadanie. –

Powiązane problemy