2012-05-24 17 views
10

Después de congelar mi aplicación rastreé la causa a un hilo que esperaba en una tarea creada por Task.Delay() (o TaskEx.Delay() en .NET 4.0) para la cual proporcionó un TimeSpan calculado que, debido a un error, en ocasiones se calculó como TimeSpan con un TotalMilliseconds de menos de o igual a -1 y mayor que -2 (es decir, en cualquier lugar entre -10000 a -19999 tics, inclusive).¿Por qué Task.Delay() permite un retraso infinito?

Parece que cuando se pasa un negativo TimeSpan que es -2 milisegundos o inferior, el método genera correctamente un ArgumentOutOfRangeException, pero cuando se proporciona un intervalo de tiempo negativo del intervalo descrito anteriormente, devuelve un Task que nunca se completa (mediante el establecimiento de el System.Threading.Timer subyacente a un dueTime de -1 que indica el infinito). Eso significa que cualquier continuación establecida en esa tarea nunca se ejecutará, y cualquier hilo defectuoso que ocurra con .Wait() en ese Task será bloqueado para siempre.

¿Qué uso posible puede tener un Task que nunca se completa? ¿Alguien esperaría tal valor de retorno? ¿No debería pasar ningún valor negativo a .Delay(), incluidos los valores en ese rango especial, arrojar un ArgumentOutOfRangeException?

+1

El documento MSDN es bastante explícito al permitir -1, por lo que parece que se comporta correctamente. No estoy seguro del caso de uso para esa sobrecarga, pero podría ser una forma de esperar una cancelación "justa" con la sobrecarga que requiere una ficha de cancelación. –

+0

@James: no es explícito al permitir -1, es explícito al no permitir valores inferiores a -1. Ni siquiera dice qué pasará si pasa -1, a diferencia de la documentación de 'System.Threading.Timer'. Casi parece que la lista documentada de excepción se generó automáticamente a partir del código fuente. Y si está esperando una cancelación 'justa', ¿por qué incluso hacer una llamada a 'Task.Delay()'? –

+0

si crees que está roto, presenta un error al conectar. Un documento que dice "menor que -1 no es válido" es explícito (para mí) al decir que -1 es válido. Si la intención fuera -1 como inválida "más bajo que 0 no es válido" hubiera sido más fácil escribir. Como el documento y el código permiten -1, creo que esto es por diseño, pero no duden en enviar un error al conectar (más probable es que el equipo de BCL los procese que un hilo SO aleatorio, creo :) :) –

Respuesta

7

Timeout.Infinite o -1 es útil cuando desea esperar indefinidamente una tarea de larga ejecución que llevará un tiempo indeterminado en completarse, pero finalmente se completará.

La API de Win32 también utiliza una constante INFINITE = -1 para tiempos de espera infinitos.

Normalmente no querría usarlo en un hilo de UI, ya que podría congelar la IU (que parece ser su problema). Pero hay casos de uso válidos en un hilo de trabajo, p. un servidor que bloquea la espera de una conexión de un cliente.

+0

Un tiempo de espera infinito es útil. Un retraso infinito no lo es (y por lo tanto, el primer párrafo no se aplica). ¿Qué diablos querrías demorar para siempre? Es mejor que no lo hagas. Simplemente no tiene ningún sentido para mí que el detalle de implementación de '-1' se propague desde' System.Threading.Timer' (o el temporizador Win32) hasta el método 'Task.Delay()'.Es contrario al principio de diseño de Microsoft de empujar a los desarrolladores al "pozo del éxito", a menos que haya algún caso de uso del que no tenga conocimiento. –

+0

Además, no lo estoy usando en un hilo de interfaz de usuario. Es un servicio de Windows que cuando se le pide que se detenga, debe realizar una rutina de apagado llamando a un método basado en [TAP] (http://www.microsoft.com/en-us/download/details.aspx?id=19957) eso puede tomar demasiado tiempo para ejecutarse, por lo que también crea una tarea de retardo que realizará un apagado forzado. A continuación, realiza un '.WaitAny()' en ambas tareas, y dado que la tarea original tomó un tiempo extremadamente largo y la tarea de demora nunca se completará (en lugar de lanzar una excepción), el servicio pareció bloquearse. –

+1

¿Cómo un servidor que está bloqueando la espera de una conexión de un cliente usaría una 'Tarea' que nunca se completa? ¿Podrías mostrar un ejemplo? –

2

En escenarios de imitación, donde quiero asegurarme de que mi código en un bloque Task.WhenAny() maneja correctamente una de las tareas esperadas, puedo burlarme de las otras tareas y usar un retraso infinito para asegurarme de que la tarea Cuando alguien está procesando la tarea, no me burlé como un retraso infinito.

+2

Interesante caso de uso, aunque creo que sería más explícito y claro utilizar 'taskCompletionSource(). Task' nuevo para proporcionar una tarea interminable. –

Cuestiones relacionadas