Framework 4.0 上的任务:在延续链中的最内层任务上设置超时
Tasks on Framework 4.0: Put a timeout on the innermost task in a chain of continuations
我一直在寻找一种基于任务的方法来检测任务超时,而无需 Wait()
。
超时应该放在连续链中最内层的任务,而异常应该只在最外层被捕获。
我的解决方案不是阻止执行,而是 returns 包装原始任务的任务,允许用户在超时时捕获异常。
所以我想出了这个代码:
public static Task<T> Timeout<T>(Task<T> baseTask, int milliseconds)
{
var timer = Delay(milliseconds);
return Task.Factory.ContinueWhenAny(
new []
{
baseTask,
timer.ContinueWith<T>(x => { throw new TaskTimeOutException(); })
},
x => x.Result
);
}
函数 Delay()
在 How to put a task to sleep (or delay) in C# 4.0? 的公认解决方案中进行了描述。
我想改进我的代码,基本上我有几个问题:
- 有人能看出可能存在的陷阱吗?
- 超时是否会导致原始任务取消?
谢谢。
编辑
根据评论,我做出了这个轻微的改进:
public static Task<T> Timeout<T>(Task<T> baseTask, int milliseconds)
{
var timer = Delay(milliseconds);
return Task.Factory.ContinueWhenAny(
new []
{
baseTask,
timer
},
x =>
{
if (x.Equals(baseTask)) return baseTask.Result;
throw new TaskTimeOutException();
}
);
}
您可以尝试使用 CancellationToken
创建任务,然后像这样调用 tokenSource.CancelAfter(...)
var tokeSource = new CancellationTokenSource();
Task.Run(() => { Console.WriteLine("processing"); }, tokenSource.Token);
tokenSource.CancelAfter(TimeSpan.FromSeconds(30));
在 .Net 4.0 中,您可以自己实现 CancelAfter
,例如
public static class CancellationTokenSourceExtensions
{
public static Task CancelAfter(this CancellationTokenSource cts, TimeSpan timeout)
{
return Task.Delay(timeout).ContinueWith(t => cts.Cancel());
}
}
个人认为基于cts
的方案更符合TPL精神
class Program
{
static bool continueRun = true;
static void Main(string[] args)
{
System.AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionTrapper;
try
{
Console.WriteLine("Enter code");
Task.Run(() =>
{
Console.WriteLine("Enter task");
System.Threading.Thread.Sleep(1000);
Console.WriteLine("End thread sleep");
throw new Exception("Inner task execution");
});
Console.WriteLine("Exit code");
}
catch (Exception err)
{
Console.WriteLine("Exception code");
}
finally
{
Console.WriteLine("Finally code");
}
Console.WriteLine("Press a key to exit");
Console.ReadLine();
}
private static void UnhandledExceptionTrapper(object sender, UnhandledExceptionEventArgs e)
{
Console.Write("Unhandled exception");
continueRun = false;
Console.ReadLine();
}
}
输出:
Enter code
Exit code
Finally code
Press a key to exit
Enter Task
End thread sleep
在 visual studio 中,您会看到引发一个未捕获的异常
我一直在寻找一种基于任务的方法来检测任务超时,而无需 Wait()
。
超时应该放在连续链中最内层的任务,而异常应该只在最外层被捕获。
我的解决方案不是阻止执行,而是 returns 包装原始任务的任务,允许用户在超时时捕获异常。
所以我想出了这个代码:
public static Task<T> Timeout<T>(Task<T> baseTask, int milliseconds)
{
var timer = Delay(milliseconds);
return Task.Factory.ContinueWhenAny(
new []
{
baseTask,
timer.ContinueWith<T>(x => { throw new TaskTimeOutException(); })
},
x => x.Result
);
}
函数 Delay()
在 How to put a task to sleep (or delay) in C# 4.0? 的公认解决方案中进行了描述。
我想改进我的代码,基本上我有几个问题:
- 有人能看出可能存在的陷阱吗?
- 超时是否会导致原始任务取消?
谢谢。
编辑
根据评论,我做出了这个轻微的改进:
public static Task<T> Timeout<T>(Task<T> baseTask, int milliseconds)
{
var timer = Delay(milliseconds);
return Task.Factory.ContinueWhenAny(
new []
{
baseTask,
timer
},
x =>
{
if (x.Equals(baseTask)) return baseTask.Result;
throw new TaskTimeOutException();
}
);
}
您可以尝试使用 CancellationToken
创建任务,然后像这样调用 tokenSource.CancelAfter(...)
var tokeSource = new CancellationTokenSource();
Task.Run(() => { Console.WriteLine("processing"); }, tokenSource.Token);
tokenSource.CancelAfter(TimeSpan.FromSeconds(30));
在 .Net 4.0 中,您可以自己实现 CancelAfter
,例如
public static class CancellationTokenSourceExtensions
{
public static Task CancelAfter(this CancellationTokenSource cts, TimeSpan timeout)
{
return Task.Delay(timeout).ContinueWith(t => cts.Cancel());
}
}
个人认为基于cts
的方案更符合TPL精神
class Program
{
static bool continueRun = true;
static void Main(string[] args)
{
System.AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionTrapper;
try
{
Console.WriteLine("Enter code");
Task.Run(() =>
{
Console.WriteLine("Enter task");
System.Threading.Thread.Sleep(1000);
Console.WriteLine("End thread sleep");
throw new Exception("Inner task execution");
});
Console.WriteLine("Exit code");
}
catch (Exception err)
{
Console.WriteLine("Exception code");
}
finally
{
Console.WriteLine("Finally code");
}
Console.WriteLine("Press a key to exit");
Console.ReadLine();
}
private static void UnhandledExceptionTrapper(object sender, UnhandledExceptionEventArgs e)
{
Console.Write("Unhandled exception");
continueRun = false;
Console.ReadLine();
}
}
输出:
Enter code
Exit code
Finally code
Press a key to exit
Enter Task
End thread sleep
在 visual studio 中,您会看到引发一个未捕获的异常