C# 调用外部方法时的任务调度

C# 调用外部方法时的任务调度,c#,async-await,C#,Async Await,我有以下扩展,它允许我对任务强制超时 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 {

我有以下扩展,它允许我对任务强制超时

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);
    }
}
我的问题是,当我在这种情况下调用外部库中的一个C++ CLR类库时,TimeOutTFter中的代码永远不会达到,任务永远不会超时。我可以通过使用下面的Task.Factory.StartNew,将回调传递给长时间运行的任务并创建我的任务来解决此问题。在开始执行主任务之前延迟任务,但这不能作为扩展

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中的代码似乎仅在其扩展的任务中包含某些内容时才执行?我可以向扩展添加哪些功能来防止这种情况发生?

我认为您的主要问题是,在尝试使用扩展方法时,您甚至都没有启动任务

一个任务是完全异步的,并且可以到达它的代码。启动任务的一种方法是在工厂中尝试使用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);
    }
}

您是在Windows窗体或WPF UI中这样做的吗?你能制作一个简短但完整的程序来演示这个问题吗?我们对您调用的方法一无所知也无济于事——它是异步的吗?您应该知道,这并不是传统意义上的超时。您的任务不会中止,只有等待超时。。任务将继续进行,如果是无限循环或其他长时间运行的任务,则可能会产生非常奇怪的副作用。在超时之后,您可能也不知道是否会在该任务中抛出异常。“JONSKET”正在执行的代码是在控制台应用程序中,任务中调用的方法是调用C++ CLR类库中的方法,该方法又调用与映像库相关联的外部DLL。在某些情况下,任务会同步执行而扩展不会被调用吗?我相信@JonSkeet的做法是正确的。检查如何调用失败的代码。如果使用等待或结果,您可能会遇到我在博客中描述的问题。作为一个简单的测试,在TimeoutAfter中的每个await表达式中添加一个.ConfigureAwaitfalse,看看它是否有效。@jimmyjambles:请给出一个最小的、可重复的示例。您永远不需要使用Startone显式启动异步任务,因为您持有对已启动任务的引用,我遇到的问题是,扩展中的任何代码都不会与某些基本任务一起调用
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");
    }
}
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);
    }
}