平台:Silverlight 4、.NET 4
随着.NET 4.5开发者预览版的发布,RegEx类已经得到了增强,可以设置超时值,以防止当匹配模式出现问题时,正则表达式引擎挂起UI。
请求建议,在.NET 4 Silverlight应用程序中实现类似功能。
提前致谢。
通用示例:
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;
}
更新:
修复了上述片段,正确处理异常情况。
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 WorthamThread.Join
只是等待指定的超时时间,然后检查线程是否仍在运行。它不会取消或中止线程,这正是我们需要的。 - Steve Wortham虽然不是很简单,但可以通过使用两个线程来完成,第一个线程执行正则表达式,第二个线程在第一个线程运行时间过长时终止第一个线程。但这本身也存在问题。
我重新实现了上面的代码,并以一种我认为更可靠的方式进行了更改。
/// <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!
}