如何为一行C#代码设置超时时间?

72

可能是重复问题:
设置操作的超时时间

如何在C#中为一行代码设置超时时间。 例如 RunThisLine(SomeMethod(Some Input), TimeSpan.FromSeconds(10)) 使用10秒的超时时间运行SomeMethod。 提前致谢。


1
这行代码是否是协作的,即它是否支持取消操作? - Daniel Hilgarth
不,行处理必须限制在指定的时间内。 - Hossein
3个回答

160

你可以使用任务并行库(TPL)。更确切地说,你可以使用Task.Wait(TimeSpan)

using System.Threading.Tasks;

var task = Task.Run(() => SomeMethod(input));
if (task.Wait(TimeSpan.FromSeconds(10)))
    return task.Result;
else
    throw new Exception("Timed out");

6
太好了。SomeMethod的结果将会在task.Result变量中。谢谢。 - Hossein
6
如果只需要超时功能且/或你的方法不处理取消标记,那么使用CancellationToken并非必要。Wait有一个重载版本没有token,可以正常工作。 - trapicki
3
这是一个好的解决方案,但它并不是停止功能。它只是通知超时问题。 - Bondaryuk Vladimir
3
即使 SomeMethod 在2秒内响应,这段代码仍然会等待10秒。 - Valerian Pereira
7
根据MSDN(请参见备注), Task.Wait方法不应该这样做,它会一直阻塞直到a)任务成功完成,b)任务抛出异常或c)任务超时。 - Carsten
显示剩余7条评论

16
您可以使用IAsyncResult和Action类/接口来实现这一点。
public void TimeoutExample()
{
    IAsyncResult result;
    Action action = () =>
    {
        // Your code here
    };

    result = action.BeginInvoke(null, null);

    if (result.AsyncWaitHandle.WaitOne(10000))
         Console.WriteLine("Method successful.");
    else
         Console.WriteLine("Method timed out.");
}

4
非常容易使用!但是如果超时,这是否会终止其他任务? - Benjamin Karlog
其他任务 = 动作 * - Benjamin Karlog

7
我使用类似以下代码(你需要添加处理各种失败情况的代码):
    var response = RunTaskWithTimeout<ReturnType>(
        (Func<ReturnType>)delegate { return SomeMethod(someInput); }, 30);


    /// <summary>
    /// Generic method to run a task on a background thread with a specific timeout, if the task fails,
    /// notifies a user
    /// </summary>
    /// <typeparam name="T">Return type of function</typeparam>
    /// <param name="TaskAction">Function delegate for task to perform</param>
    /// <param name="TimeoutSeconds">Time to allow before task times out</param>
    /// <returns></returns>
    private T RunTaskWithTimeout<T>(Func<T> TaskAction, int TimeoutSeconds)
    {
        Task<T> backgroundTask;

        try
        {
            backgroundTask = Task.Factory.StartNew(TaskAction);
            backgroundTask.Wait(new TimeSpan(0, 0, TimeoutSeconds));
        }
        catch (AggregateException ex)
        {
            // task failed
            var failMessage = ex.Flatten().InnerException.Message);
            return default(T);
        }
        catch (Exception ex)
        {
            // task failed
            var failMessage = ex.Message;
            return default(T);
        }

        if (!backgroundTask.IsCompleted)
        {
            // task timed out
            return default(T);
        }

        // task succeeded
        return backgroundTask.Result;
    }

7
请注意:这不会在超时后取消操作。我并不是说它应该这样做,而是因为我认为这是一个重要的细节,所以提到了它。 - Daniel Hilgarth
你也可以使用Task.CancelAfter并检查取消。 - Nick Turner

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接