【发布时间】:2015-01-04 05:25:36
【问题描述】:
我有以下扩展,它允许我强制任务超时。
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() 中的代码似乎只在某些内容位于它正在扩展的任务中时才执行?我可以向扩展程序添加什么功能来防止这种情况发生?
【问题讨论】:
-
您是否有机会在 Windows 窗体或 WPF UI 中执行此操作?你能制作一个简短但完整的程序来演示这个问题吗?我们对您调用的方法一无所知,这无济于事 - 它是异步的吗?#
-
您应该知道,这并不是传统意义上的“超时”。您的任务不会中止,只是您的等待超时.. 任务将继续进行,如果它是无限循环或其他长时间运行的任务,它可能会产生非常奇怪的副作用。您可能也不知道超时后是否会在该任务中引发异常。
-
@JonSkeet 正在执行的代码在控制台应用程序中,任务中调用的方法是调用 c++ CLR 类库中的方法,该类库又调用与映像库关联的外部 dll .是否存在某些条件会同步执行任务并且不会调用扩展?
-
我相信@JonSkeet 是在正确的轨道上。检查您的失败代码是如何被调用的。如果
Wait或Result有任何用途,您可能会遇到我在博客中描述的classic deadlock issue。作为一个简单的测试,将.ConfigureAwait(false)添加到TimeoutAfter中的每个await表达式,看看是否有效。 -
@jimmyjambles:请发布一个最小的、可重现的示例。
标签: c# async-await