在 .NET Framework 4.0 中实现方法超时控制,有几种常用方案。下面介绍几种实现方式:
方案一:使用 Task + CancellationToken(推荐)
csharp
using System;
using System.Threading;
using System.Threading.Tasks;
public class TimeoutExample
{
public string ExecuteWithTimeout(int timeoutMs)
{
var cts = new CancellationTokenSource();
var token = cts.Token;
var task = Task.Factory.StartNew(() =>
{
// 在子方法中需要检查 token.IsCancellationRequested
DoWork(token);
return "完成";
}, token);
// 等待任务完成或超时
bool completed = task.Wait(timeoutMs);
if (!completed)
{
cts.Cancel(); // 发送取消信号
return "超时退出";
}
return task.Result;
}
private void DoWork(CancellationToken token)
{
for (int i = 0; i < 100; i++)
{
// 定期检查取消请求
if (token.IsCancellationRequested)
{
return; // 或 throw new OperationCanceledException(token);
}
Thread.Sleep(100); // 模拟耗时操作
SubMethod(token); // 子方法也传递 token
}
}
private void SubMethod(CancellationToken token)
{
token.ThrowIfCancellationRequested();
// 执行操作...
}
}
方案二:使用 Thread.Abort(强制终止,不推荐但有效)
csharp
using System;
using System.Threading;
public class TimeoutWithAbort
{
public string ExecuteWithTimeout(int timeoutMs)
{
string result = null;
Exception exception = null;
Thread workerThread = new Thread(() =>
{
try
{
result = DoLongRunningWork();
}
catch (ThreadAbortException)
{
Thread.ResetAbort(); // 重置中止状态
result = "被中止";
}
catch (Exception ex)
{
exception = ex;
}
});
workerThread.Start();
// 等待线程完成或超时
bool completed = workerThread.Join(timeoutMs);
if (!completed)
{
workerThread.Abort(); // 强制终止线程
return "超时退出";
}
if (exception != null)
throw exception;
return result;
}
private string DoLongRunningWork()
{
// 即使不检查任何标志,也会被 Abort 中断
Thread.Sleep(10000);
return "完成";
}
}
方案三:封装成通用工具类
csharp
using System;
using System.Threading;
public static class TimeoutHelper
{
/// <summary>
/// 带超时执行方法
/// </summary>
public static T Execute<T>(Func<T> func, int timeoutMs, T defaultValue = default(T))
{
T result = defaultValue;
Exception exception = null;
Thread thread = new Thread(() =>
{
try
{
result = func();
}
catch (ThreadAbortException)
{
Thread.ResetAbort();
}
catch (Exception ex)
{
exception = ex;
}
});
thread.Start();
if (!thread.Join(timeoutMs))
{
thread.Abort();
return defaultValue;
}
if (exception != null)
throw exception;
return result;
}
/// <summary>
/// 带超时执行无返回值方法
/// </summary>
public static bool Execute(Action action, int timeoutMs)
{
Exception exception = null;
Thread thread = new Thread(() =>
{
try
{
action();
}
catch (ThreadAbortException)
{
Thread.ResetAbort();
}
catch (Exception ex)
{
exception = ex;
}
});
thread.Start();
if (!thread.Join(timeoutMs))
{
thread.Abort();
return false; // 超时
}
if (exception != null)
throw exception;
return true; // 正常完成
}
}
// 使用示例
public class Program
{
public void Main()
{
// 方式1:有返回值
var result = TimeoutHelper.Execute(() =>
{
Thread.Sleep(5000);
return "完成";
}, 2000, "超时默认值");
// 方式2:无返回值
bool success = TimeoutHelper.Execute(() =>
{
DoSomething();
}, 3000);
if (!success)
{
Console.WriteLine("操作超时");
}
}
private void DoSomething() { }
}
方案对比
| 方案 | 优点 | 缺点 |
|---|---|---|
| CancellationToken | 安全、协作式取消、资源可正确释放 | 需要子方法配合检查 token |
| Thread.Abort | 可强制终止,无需子方法配合 | 可能导致资源泄漏、状态不一致 |
建议
- 如果能控制所有子方法代码 :优先使用
CancellationToken,在关键点检查取消状态 - 如果调用第三方库或无法修改子方法 :使用
Thread.Abort,但要注意资源清理 - 对于数据库操作 :使用
SqlCommand.CommandTimeout等内置超时机制