我有以下扩展名,允许我强制执行任务超时。
public static async Task<T> TimeoutAfter<T>(this Task<T> task, TimeSpan timeSpan, string message = null)
{
if (task == await Task.WhenAny(task, Task.Delay(timeSpan)))
return await task;
else
{
throw new TimeoutException(message);
}
}
我有以下任务:
private async Task<string> TestTask(PageData page)
{
await Task.Delay(TimeSpan.FromSeconds(10));
return "teststring";
}
当我使用以下内容调用任务时,我得到了预期的结果。
var teststring = await TestTask()
.TimeoutAfter(TimeSpan.FromSeconds(5), "timeout message");
我发出的问题是当我在外部库中调用一个方法(在这种情况下是一个c ++ CLR类库)时,永远不会到达TimeoutAfter()中的代码,并且任务永远不会超时。我可以通过使用Task.Factory.StartNew()(下面)解决问题,将回调传递给长时间运行的任务并在主任务的执行开始之前创建我的Task.Delay任务,但这不会起作用延期。
private async Task<T> Timeout<T>(Func<Task<T>> taskFunc)
{
var taskWait = Task.Delay(TimeSpan.FromSeconds(15));
var task = Task.Factory.StartNew(() => taskFunc());
if (task == await Task.WhenAny(task, taskWait))
return await (await task);
else
{
throw new TimeoutException("timeout");
}
}
为什么看起来TimeoutAfter()中的代码只在特定内容在其正在扩展的任务中时执行?我可以在扩展中添加哪些功能来防止这种情况发生?
答案 0 :(得分:-2)
我认为您的主要问题是,当您尝试使用扩展方法时,您甚至都没有开始执行任务。
Task
完全异步,must be started可以访问其代码。启动任务的一种方法是在尝试使用工厂时使用StartNew。
但您也可以调用Start
方法。您唯一需要注意的是,如果已经启动或已经完成启动,则在调用Start时会出现异常。
所以,我认为您的解决方案可以是:
public static async Task<T> TimeoutAfter<T>(this Task<T> task, TimeSpan timeSpan, string message = null)
{
if(task.Status != TaskStatus.Running && task.Status != TaskStatus.RanToCompletion){
task.Start();
}
if (task == await Task.WhenAny(task, Task.Delay(timeSpan)))
return await task;
else
{
throw new TimeoutException(message);
}
}