2012-05-24 7 views
10

Dopo che l'applicazione si è bloccata, ho rintracciato la causa in un thread in attesa di un'attività creata da Task.Delay() (o TaskEx.Delay() in .NET 4.0) per la quale è stato fornito uno TimeSpan calcolato che, a causa di un errore, è stato occasionalmente calcolato su TimeSpan con un numero di TotalMilliseconds inferiore o uguale a -1 e superiore a -2 (ovvero compreso tra -10000 e -19999 tick, inclusi).Perché Task.Delay() consente un ritardo infinito?

Sembra che quando si passa un negativo TimeSpan che è -2 millisecondi o più bassa, il metodo genera correttamente un ArgumentOutOfRangeException, ma quando si fornisce un periodo negativo della gamma descritta sopra, restituisce un Task che non viene completata (impostando il sottostante System.Threading.Timer a un dueTime di -1 che denota l'infinito). Ciò significa che qualsiasi continuazione impostata su quell'attività non verrà mai eseguita e qualsiasi thread scadente che si verifica in .Wait() su quello Task verrà bloccato per sempre.

Quale possibile utilizzo può avere un Task che non ha mai completato? Qualcuno dovrebbe aspettarsi un simile valore di ritorno? Non deve essere passato alcun valore negativo a .Delay(), inclusi i valori in quell'intervallo speciale, un ArgumentOutOfRangeException?

+1

Il documento MSDN è piuttosto esplicito nel consentire -1, quindi sembra che si stia comportando correttamente. Non sono sicuro del caso d'uso per quel sovraccarico, ma potrebbe essere un modo di aspettare la cancellazione 'giusta' con il sovraccarico che richiede un token di cancellazione. –

+0

@James: Non è esplicito nel consentire -1, è esplicito nel disabilitare valori inferiori a -1. Non dice nemmeno cosa succederà se passi -1, a differenza della documentazione di 'System.Threading.Timer'. Sembra quasi che l'elenco di eccezioni documentato sia stato generato automaticamente dal codice sorgente. E se stai aspettando la "giusta" cancellazione, perché anche chiamare a 'Task.Delay()'? –

+0

se pensi che sia rotto, segnala un bug alla connessione. Un documento che dice "inferiore a -1 non valido" è esplicito (per me) nel dire che -1 è valido. Se l'intento era -1 come non valido "inferiore a 0 non valido" sarebbe stato più facile scrivere. Dal momento che il doc e il codice consentono entrambi -1, penso che questo sia By Design, ma sentitevi liberi di inviare un errore al collegamento (più probabile che venga elaborato dal team BCL piuttosto che un thread SO casuale, penserei :) –

risposta

7

Timeout.Infinite o -1 è utile quando si desidera attendere indefinitamente per un'attività a esecuzione prolungata che richiederà un tempo indeterminato per il completamento, ma alla fine verrà completata.

L'API Win32 utilizza anche una costante INFINITE = -1 per timeout infiniti.

Normalmente non si vorrebbe usarlo in un thread dell'interfaccia utente, in quanto potrebbe bloccare l'interfaccia utente (che sembra essere il tuo problema). Ma ci sono casi d'uso validi in un thread di lavoro - ad es. un server che sta bloccando in attesa di una connessione da un client.

+0

Un timeout infinito è utile. Un ritardo infinito no (e quindi il primo paragrafo non si applica). In che parte del mondo vorresti rimandare per sempre? Potresti anche non farlo. Non ha alcun senso per me che i dettagli di implementazione di '-1' saranno propagati da' System.Threading.Timer' (o il timer Win32) fino al metodo 'Task.Delay()'.È contrario al principio di progettazione di Microsoft di spingere gli sviluppatori nella "fossa del successo", a meno che non ci sia qualche caso d'uso di cui non sono a conoscenza. –

+0

Inoltre, non lo sto utilizzando in un thread dell'interfaccia utente. È un servizio Windows che quando viene richiesto di interrompere, deve eseguire una routine di spegnimento chiamando un metodo [TAP] (http://www.microsoft.com/en-us/download/details.aspx?id=19957) potrebbe richiedere troppo tempo per essere eseguito in modo da creare anche un'attività di ritardo che eseguirà un arresto forzato. Quindi esegue un '.WaitAny()' su entrambe le attività, e poiché l'attività originale ha impiegato un tempo estremamente lungo e l'attività di ritardo non verrà mai completata (invece di lanciare un'eccezione), il servizio sembrerebbe bloccarsi. –

+1

In che modo un server che sta bloccando in attesa di una connessione da un client utilizza un 'Task' che non viene mai completato? Potresti mostrare un esempio? –

2

In scenari di simulazione, in cui voglio essere certo che il mio codice in un blocco Task.WhenAny() stia gestendo correttamente una delle attività attese, posso prendere in giro le altre attività e usare un ritardo infinito per assicurare che l'operazione . Quando qualcuno sta elaborando il compito non mi sono preso gioco di un ritardo infinito.

+2

Caso d'uso interessante, anche se penso che sarebbe più esplicito e chiaro usare 'new TaskCompletionSource(). Task' per fornire un'attività senza fine. –