2012-02-27 10 views
6

Plataforma: Silverlight 4, .NET 4Ejecución RegEx Tiempo de espera en .NET 4

Con la vista previa .NET 4.5 Developer la clase RegEx se ha mejorado para permitir la fijación de un valor de tiempo de espera que evitaría que el motor de expresiones regulares de colgar la interfaz de usuario si hay problemas con la coincidencia de patrones.

Solicitando sugerencias para implementar una funcionalidad similar en una aplicación .NET 4 Silverlight.

Gracias de antemano.

+0

Véase también esta respuesta usando la tarea: http://stackoverflow.com/a/13526507/492 ... No sé si funciona en Silverlight. –

Respuesta

10

ejemplo genérico:

public static R WithTimeout<R>(Func<R> proc, int duration) 
{ 
    var wh = proc.BeginInvoke(null, null); 

    if (wh.AsyncWaitHandle.WaitOne(duration)) 
    { 
    return proc.EndInvoke(wh); 
    } 

    throw new TimeOutException(); 
} 

Uso:

var r = WithTimeout(() => regex.Match(foo), 1000); 

Actualización:

Como ha señalado Christian.K, el hilo asíncrono será aún continúan en ejecución .

Aquí es uno donde el hilo terminará:

public static R WithTimeout<R>(Func<R> proc, int duration) 
{ 
    var reset = new AutoResetEvent(false); 
    var r = default(R); 
    Exception ex = null; 

    var t = new Thread(() => 
    { 
    try 
    { 
     r = proc(); 
    } 
    catch (Exception e) 
    { 
     ex = e; 
    } 
    reset.Set(); 
    }); 

    t.Start(); 

    // not sure if this is really needed in general 
    while (t.ThreadState != ThreadState.Running) 
    { 
    Thread.Sleep(0); 
    } 

    if (!reset.WaitOne(duration)) 
    { 
    t.Abort(); 
    throw new TimeoutException(); 
    } 

    if (ex != null) 
    { 
    throw ex; 
    } 

    return r; 
} 

Actualización:

fija por encima de fragmento de tratar correctamente con excepciones.

+2

¿Pero no continuará esto (también) ejecutándose en segundo plano si se produce un tiempo de espera? –

+0

@ Christian.K: ¡Eso creía, pero parece que tienes razón!Gracias :) ¡De vuelta al tablero de dibujo para este! – leppie

+0

@ Christian.K: respuesta actualizada :) – leppie

3

No es tan simple, pero se puede hacer utilizando dos hilos con el primero haciendo la expresión regular, el segundo matando al primer hilo si se prolonga demasiado. Sin embargo, esto es problemático en sí mismo.

+1

+1 para reconocer "Esto es problemático en sí mismo, sin embargo". :-) –

+0

Tristemente, es el único que lo hace sin el método regex que lo soporta; (aunque es algo muy bueno tener, pero ... bueno ... .NET 4.5 aquí vengo ... esta semana – TomTom

1

La forma estándar de obtener un tiempo de espera en algo que aún no viene con la característica es simplemente iniciar lo que desee procesar en una secuencia separada, y luego en su cadena principal, utilice Thread.Join con el tiempo de espera apropiado.

+1

Pero tenga en cuenta que el otro hilo no deja de ejecutarse, simplemente porque expiró su tiempo de espera de unión. Si uno puede vivir con eso, está bien. Sin embargo, dependiendo de la frecuencia, los hilos de expresiones regulares pueden iniciarse (usuario impaciente haciendo clic en un enlace/botón), que puede conducir a un montón de subprocesos en ejecución y recursos utilizados. Por no hablar de expresiones regulares en ejecución "quemar" la CPU en el fondo. –

+0

Obviamente, si no desea que continúe, entonces agrega un Thread.Abort after the timeout ... Pero en algunos casos es posible que solo desee mostrar una advertencia en la GUI diciendo "Esto está tardando más de lo esperado" – Cine

+0

Sí, pero esa parte "obvia" es lo que el nuevo Regex-timeout el soporte en .NET 4.5 lo hace, y el disco duro ('Thread.Abort' tiene su su propio conjunto de cuestiones). –

2

He vuelto a implementar el código anterior cambiándolo de una manera que creo que es más confiable.

/// <summary> 
    /// Executes function proc on a separate thread respecting the given timeout value. 
    /// </summary> 
    /// <typeparam name="R"></typeparam> 
    /// <param name="proc">The function to execute.</param> 
    /// <param name="timeout">The timeout duration.</param> 
    /// <returns></returns> 
    public static R ExecuteWithTimeout<R>(Func<R> proc, TimeSpan timeout) { 
     var r = default(R); // init default return value 
     Exception ex = null; // records inter-thread exception 

     // define a thread to wrap 'proc' 
     var t = new Thread(() => { 
      try { 
       r = proc(); 
       } 
      catch (Exception e) { 
       // this can get set to ThreadAbortException 
       ex = e; 

       Debug.WriteLine("Exception hit"); 

       } 
      }); 

     t.Start(); // start running 'proc' thread wrapper 
     // from docs: "The Start method does not return until the new thread has started running." 

     if (t.Join(timeout) == false) { 
      t.Abort(); // die evil thread! 
      // Abort raises the ThreadAbortException 
      int i = 0; 
      while ((t.Join(1) == false) && (i < 20)) { // 20 ms wait possible here 
       i++; 
       } 
      if (i >= 20) { 
       // we didn't abort, might want to log this or take some other action 
       // this can happen if you are doing something indefinitely hinky in a 
       // finally block (cause the finally be will executed before the Abort 
       // completes. 
       Debug.WriteLine("Abort didn't work as expected"); 
       } 
      } 

     if (ex != null) { 
      throw ex; // oops 
      } 
     return r; // ah! 
     } 
Cuestiones relacionadas