背景:
前些日子写了一个利用正则表达式来提取内容的程序,要求正则表达式可以灵活地配置,但事后在运行时发现一个问题:有的正则表达式写的效率低下,将CPU占用了100%,并且执行了很长时间也没有执行完,影响了其它任务的工作.于是想把执行正则表达式的代码放在一个可以在超时时自动中断的函数中.
首先想到的是每次使用线程System.Threading.Thread来定义一个新的线程,启动并执行,然后用ManualResetEvent等待它执行一段指定的时间,如果没有执行完,则调用该线程的Abort来中断它.
但大多数正则表达式执行的效率很高,这样每次都开一个线程会影响效率,于是想到了利用线程池线程来执行它,但在这儿遇到了难题,线程池线程只有在运行的时候才会利用Thread.CurrentThread来获得当前的线程(以便在超时时调用它的Abort方法),.
这种做法是很危险的时候,可能在超时之后,当我们调用Abort方法的那一瞬间,该线程已经完成了执行,并且又在执行其它任务了,于是中断了不该中断的任务.
思前想后,想到了利用两个ManualResetEvent来保证不在Abort时中断其它任务的方法:
代码:
delegate void Delegate();
/// <summary>
/// 执行指定的方法,如果在指定的时间之内没有完成,则中止
/// </summary>
/// <param name="func">任务过程</param>
/// <param name="timeSpan">超时时间</param>
/// <param name="timeoutCallback">如果超时,则调用该方法</param>
/// <returns>是否正确执行完毕</returns>
public static bool Call(Delegate func, TimeSpan timeSpan, Delegate timeoutCallback)
{
if (func == null)
throw new ArgumentNullException("func");
ManualResetEvent resetEvent = new ManualResetEvent(false);
ManualResetEvent waitThreadEvent = new ManualResetEvent(false);
Exception error = null;
Thread thread = null;
// 将任务加到线程当中
ThreadPool.QueueUserWorkItem(delegate {
thread = Thread.CurrentThread;
try { func(); }
catch (ThreadAbortException) { }
catch (Exception ex) { error = ex; }
resetEvent.Set();
waitThreadEvent.WaitOne(); // 每次线程执行结束都等待后续的处理逻辑
});
try
{
bool result = resetEvent.WaitOne(timeSpan, false); // 等待任务的结束
if (error != null) // 说明在执行过程中出现异常,直接抛出异常
throw error;
if (!result)
{
if (thread != null)
{
thread.Abort(); // 此时可以确保该线程没有开始运行新的任务
waitThreadEvent.Set();
}
if (timeoutCallback != null)
timeoutCallback();
}
return result;
}
finally
{
waitThreadEvent.Set(); // 最后确保释放线程池线程
}
}