2012-02-27 2 views
6

Платформа: Silverlight 4, .NET 4Реализация RegEx Timeout в .NET 4

С .NET 4.5 Developer Preview RegEx класс добавлена ​​возможность настройки значения тайм-аута, который бы предотвратить RegEx двигатель от виселицы пользовательский интерфейс, если есть проблемы с сопоставлением шаблонов.

Запрос предложений по реализации аналогичной функциональности в приложении .NET 4 Silverlight.

Заранее спасибо.

+0

См. Также этот ответ, используя задачу: http://stackoverflow.com/a/13526507/492 ... Я не знаю, работает ли это в Silverlight. –

ответ

10

Общий пример:

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(); 
} 

Использование:

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

Update:

Как отметил Christian.K, Асинхронный поток будет по-прежнему продолжать работать ,

Вот одна где нить закончится:

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; 
} 

Update:

Fixed выше фрагмент кода, чтобы иметь дело с исключениями правильно.

+2

Но не будет ли это продолжаться в фоновом режиме, если произойдет тайм-аут? –

+0

@ Christian.K: Я так и думал, но, похоже, вы правы!Спасибо :) Вернуться к чертежной доске для этого! – leppie

+0

@ Christian.K: Обновлен ответ :) – leppie

3

Это не так просто - но это может быть сделано с использованием двух потоков с первым выполнением регулярного выражения, второе - первым, если он слишком длинный. Это само по себе проблематично.

+1

+1 для подтверждения «Это само по себе проблематично». :-) –

+0

К сожалению, это единственный способ сделать это без поддерживающего его метода regex (но это очень хорошо, но ... но ... .NET 4.5 здесь я прихожу ... на этой неделе – TomTom

1

Стандартный способ получить тайм-аут на то, что еще не приходит с этой функцией, - это просто запустить все, что вы хотите обработать, в отдельном потоке, а затем в своем основном потоке вы используете Thread.Join с соответствующий тайм-аут.

+1

Но имейте в виду, что другой поток не прекращает работу, только потому, что ваше время окончания соединения истекло.Если вы можете жить с этим, отлично. Однако, в зависимости от потоков регулярных выражений, которые меня запускают (нетерпеливый пользователь, нажимая ссылку /), что может привести к целым пучкам потоков и используемых ресурсов. Не говоря уже о том, что в текущем цикле регулярные выражения «горят» в фоновом режиме. –

+0

Очевидно, что если вы не хотите, чтобы это продолжалось, вы добавляете Thread.Abort после таймаута ... Но в некоторых случаях вам может просто понадобиться выпустить предупреждение в графическом интерфейсе, в котором говорится: «Это занимает больше времени, чем ожидалось» – Cine

+0

Да, но эта «очевидная» часть - это то, что новый тайм-аут Regex поддержка в .NET 4.5 делает - и сложная (у «Thread.Abort» есть собственный набор проблем). –

2

Я переделал вышеуказанный код, изменив его таким образом, что я верю более надежно.

/// <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! 
     }