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

8

平台:Silverlight 4、.NET 4

随着.NET 4.5开发者预览版的发布,RegEx类已经得到了增强,可以设置超时值,以防止当匹配模式出现问题时,正则表达式引擎挂起UI。

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

提前致谢。


请参考使用Task的此答案:http://stackoverflow.com/a/13526507/492 … 我不知道它在Silverlight中是否有效。 - CAD bloke
4个回答

13

通用示例:

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

更新:

修复了上述片段,正确处理异常情况。


3
如果超时发生,这个程序(也)会继续在后台运行,是吗? - Christian.K
2
干得好。不过,关于这个问题有几点需要注意。在你的示例中调用t.Abort()会导致MethodAccessException异常,如此处所述... http://msdn.microsoft.com/en-us/library/ty8d3wta(v=vs.95).aspx 只有在运行具有提升信任的Silverlight 5应用程序时才不会出现这种情况,如此处所述... http://msdn.microsoft.com/en-us/library/ee721083(v=vs.95).aspx 我希望这也适用于在提升信任下运行的Silverlight 4,但似乎并不是这样。我在部分信任下也遇到了相同的异常。 - Steve Wortham
1
@SteveWortham:谢谢你提供的有用信息 :) 所以我猜在部分信任下没有办法中止 Silverlight 中的线程?这似乎是违反直觉的。 - leppie
好的,Thread.Join只是等待指定的超时时间,然后检查线程是否仍在运行。它不会取消或中止线程,这正是我们需要的。 - Steve Wortham
@SteveWortham:这似乎比它值得的更加痛苦;p - leppie
显示剩余3条评论

3

虽然不是很简单,但可以通过使用两个线程来完成,第一个线程执行正则表达式,第二个线程在第一个线程运行时间过长时终止第一个线程。但这本身也存在问题。


2
只翻译文本内容:+1,认可“这本身就有问题”。 :-) - Christian.K
很遗憾,这是唯一的方法,没有正则表达式支持它;(虽然这是一个非常好的功能,但是...好吧... .NET 4.5,我来了...这个星期。 - TomTom

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

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

2
请注意,即使您的加入超时已过期,其他线程也不会停止运行。如果可以接受这一点,那就没问题了。然而,根据正则表达式线程启动的频率(不耐烦的用户点击链接/按钮),可能会导致大量运行中的线程和使用的资源。更不用说在后台运行的“失控”正则表达式“烧毁”CPU了。 - Christian.K
显然,如果您不希望它继续运行,则可以在超时后添加Thread.Abort...但在某些情况下,您可能只想在GUI上弹出警告,提示“这比预期的时间要长”。 - Cine
是的,但是在.NET 4.5中新的正则表达式超时支持所做的就是那个“显而易见”的部分 - 而困难的部分(Thread.Abort有它自己的问题)。 - Christian.K

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接