在 .NET 4 中实现正则表达式超时

Posted

技术标签:

【中文标题】在 .NET 4 中实现正则表达式超时【英文标题】:Implementing RegEx Timeout in .NET 4 【发布时间】:2012-03-16 16:02:59 【问题描述】:

平台:Silverlight 4、.NET 4

在 .NET 4.5 开发人员预览版中,RegEx 类已得到增强,允许设置超时值,如果模式匹配出现问题,这将防止 RegEx 引擎挂起 UI。

请求建议以在 .NET 4 Silverlight 应用程序中实现类似功能。

提前致谢。

【问题讨论】:

另请参阅使用任务的答案:***.com/a/13526507/492 ...我不知道它是否适用于 Silverlight。 【参考方案1】:

通用示例:

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

更新:

正如 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;

更新:

在 sn-p 上方修复以正确处理异常。

【讨论】:

但是如果发生超时,这(也)不会继续在后台运行吗? @Christian.K:我是这么认为的,但看来你是对的!谢谢 :) 回到这个绘图板! @Christian.K:更新答案:) 做得很好。不过,对此有几点说明。在您的示例中调用 t.Abort() 将导致 MethodAccessException,如此处所述...msdn.microsoft.com/en-us/library/ty8d3wta(v=vs.95).aspx 唯一不会发生这种情况的情况是,它是以提升的信任运行的 Silverlight 5 应用程序,如此处所述...msdn.microsoft.com/en-us/library/ee721083(v=vs.95).aspx我是希望这也可以在提高信任度的情况下与 Silverlight 4 一起使用,但似乎没有。我得到了与部分信任相同的异常。 @SteveWortham:感谢您提供的有用信息 :) 所以我想在部分信任下没有办法中止 Silverlight 中的线程?似乎违反直觉。【参考方案2】:

这不是那么简单 - 但它可以使用两个线程来完成,第一个线程执行正则表达式,第二个线程如果运行时间过长则杀死第一个线程。不过,这本身就有问题。

【讨论】:

+1 表示承认“这本身就有问题”。 :-) 遗憾的是,它是唯一没有支持它的正则表达式方法的 wo ;(虽然拥有它是一件非常好的事情,但是......好吧...... .NET 4.5 我来了。 ..这周。【参考方案3】:

我重新实现了上面的代码,以我认为更可靠的方式对其进行了更改。

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

【讨论】:

【参考方案4】:

在该功能尚未附带的某些内容上获得超时的标准方法是简单地在单独的线程上启动您想要处理的任何内容,然后在您的主线程中使用 Thread.Join 和适当的超时。

【讨论】:

但请记住,其他线程不会停止运行,只是因为您的加入超时已过期。如果一个人能忍受它,那很好。但是,根据我启动正则表达式线程的频率(不耐烦的用户单击链接/按钮),这可能会导致一大堆正在运行的线程和使用的资源。更不用说失控的正则表达式在后台“燃烧” CPU。 显然,如果您不希望它继续,那么您可以在超时后添加一个 Thread.Abort... 但在某些情况下,您可能只想在 GUI 上弹出一个警告说“这花费的时间比预期的要长” 是的,但是“显而易见”的部分是 .NET 4.5 中新的正则表达式超时支持所做的 - 也是最困难的部分(Thread.Abort 有它自己的一系列问题)。

以上是关于在 .NET 4 中实现正则表达式超时的主要内容,如果未能解决你的问题,请参考以下文章

正则表达式:如何在 PL/SQL 中实现负向后查找

如何在没有正则表达式的情况下在 C++ 中实现有效的全字字符串替换?

正则进阶

字典字段服务器端的 C# 正则表达式验证 - .Net Core MVC

使用正则表达式拆分数学表达式

Python处理正则表达式超时的办法