异步任务超时<T> 额外的异常处理
Timeout for asynchronous Task<T> with additional exception handling
在我的项目中,我从动态 link 库中引用类型和接口。
使用这个特定库时,我必须做的第一件事是创建 EA.Repository
的实例,它在库中定义并作为进一步使用的入口点。
实例化 EA.Repository repository = new EA.Repository()
在后台执行一些复杂的操作,我发现自己面临三种可能的结果:
- 实例化需要一些时间,但最终成功完成
- 抛出异常(立即或一段时间后)
- 实例化永远阻塞(在这种情况下我想取消并通知用户)
我能够使用 Task
:
想出一个异步方法
public static void Connect()
{
// Do the lengthy instantiation asynchronously
Task<EA.Repository> task = Task.Run(() => { return new EA.Repository(); });
bool isCompletedInTime;
try
{
// Timeout after 5.0 seconds
isCompletedInTime = task.Wait(5000);
}
catch (Exception)
{
// If the instantiation fails (in time), throw a custom exception
throw new ConnectionException();
}
if (isCompletedInTime)
{
// If the instantiation finishes in time, store the object for later
EapManager.Repository = task.Result;
}
else
{
// If the instantiation did not finish in time, throw a custom exception
throw new TimeoutException();
}
}
(我知道,您可能已经在这里发现了很多问题。请耐心等待...建议将不胜感激!)
这种方法到目前为止有效 - 我可以模拟 "exception" 和 "timeout" 场景,并且我获得了所需的行为。
但是,我发现了另一种极端情况:假设实例化任务花费的时间足够长,以至于超时到期,然后引发异常。在这种情况下,我有时会以 AggregateException
表示任务未被观察到。
我正在努力寻找可行的解决方案。当超时到期时,我无法真正取消任务,因为阻塞实例化显然阻止我使用 CancellationToken
方法。
我唯一能想到的就是在抛出我的自定义 TimeoutException
:
之前开始异步观察任务(即开始另一个任务)
Task observerTask = Task.Run(() => {
try { task.Wait(); }
catch (Exception) { }
});
throw new TimeoutException();
当然,如果实例化真的永远阻塞,我已经有第一个任务永远不会完成。有了观察者任务,我现在连两个都没有了!
我对这整个方法很不安全,所以欢迎任何建议!
非常感谢您!
我不确定我是否完全理解您想要实现的目标,但是如果您这样做会怎么样 -
public static void Connect()
{
Task<EA.Repository> _realWork = Task.Run(() => { return new EA.Repository(); });
Task _timeoutTask = Task.Delay(5000);
Task.WaitAny(new Task[]{_realWork, timeoutTask});
if (_timeoutTask.Completed)
{
// timed out
}
else
{
// all good, access _realWork.Result
}
}
或者你甚至可以缩短一点 -
public static void Connect()
{
Task<EA.Repository> _realWork = Task.Run(() => { return new EA.Repository(); });
var completedTaskIndex = Task.WaitAny(new Task[]{_realWork}, 5000);
if (completedTaskIndex == -1)
{
// timed out
}
else
{
// all good, access _realWork.Result
}
}
您也可以始终 call Task.Run
with a CancellationToken
超时,但这会引发异常 - 上述解决方案可让您控制行为而不会引发异常(即使您始终可以 try/catch
)
这是一个扩展方法,您可以使用它来显式观察未观察到时可能会失败的任务:
public static Task<T> AsObserved<T>(this Task<T> task)
{
task.ContinueWith(t => t.Exception);
return task;
}
用法示例:
var task = Task.Run(() => new EA.Repository()).AsObserved();
在我的项目中,我从动态 link 库中引用类型和接口。
使用这个特定库时,我必须做的第一件事是创建 EA.Repository
的实例,它在库中定义并作为进一步使用的入口点。
实例化 EA.Repository repository = new EA.Repository()
在后台执行一些复杂的操作,我发现自己面临三种可能的结果:
- 实例化需要一些时间,但最终成功完成
- 抛出异常(立即或一段时间后)
- 实例化永远阻塞(在这种情况下我想取消并通知用户)
我能够使用 Task
:
public static void Connect()
{
// Do the lengthy instantiation asynchronously
Task<EA.Repository> task = Task.Run(() => { return new EA.Repository(); });
bool isCompletedInTime;
try
{
// Timeout after 5.0 seconds
isCompletedInTime = task.Wait(5000);
}
catch (Exception)
{
// If the instantiation fails (in time), throw a custom exception
throw new ConnectionException();
}
if (isCompletedInTime)
{
// If the instantiation finishes in time, store the object for later
EapManager.Repository = task.Result;
}
else
{
// If the instantiation did not finish in time, throw a custom exception
throw new TimeoutException();
}
}
(我知道,您可能已经在这里发现了很多问题。请耐心等待...建议将不胜感激!)
这种方法到目前为止有效 - 我可以模拟 "exception" 和 "timeout" 场景,并且我获得了所需的行为。
但是,我发现了另一种极端情况:假设实例化任务花费的时间足够长,以至于超时到期,然后引发异常。在这种情况下,我有时会以 AggregateException
表示任务未被观察到。
我正在努力寻找可行的解决方案。当超时到期时,我无法真正取消任务,因为阻塞实例化显然阻止我使用 CancellationToken
方法。
我唯一能想到的就是在抛出我的自定义 TimeoutException
:
Task observerTask = Task.Run(() => {
try { task.Wait(); }
catch (Exception) { }
});
throw new TimeoutException();
当然,如果实例化真的永远阻塞,我已经有第一个任务永远不会完成。有了观察者任务,我现在连两个都没有了!
我对这整个方法很不安全,所以欢迎任何建议!
非常感谢您!
我不确定我是否完全理解您想要实现的目标,但是如果您这样做会怎么样 -
public static void Connect()
{
Task<EA.Repository> _realWork = Task.Run(() => { return new EA.Repository(); });
Task _timeoutTask = Task.Delay(5000);
Task.WaitAny(new Task[]{_realWork, timeoutTask});
if (_timeoutTask.Completed)
{
// timed out
}
else
{
// all good, access _realWork.Result
}
}
或者你甚至可以缩短一点 -
public static void Connect()
{
Task<EA.Repository> _realWork = Task.Run(() => { return new EA.Repository(); });
var completedTaskIndex = Task.WaitAny(new Task[]{_realWork}, 5000);
if (completedTaskIndex == -1)
{
// timed out
}
else
{
// all good, access _realWork.Result
}
}
您也可以始终 call Task.Run
with a CancellationToken
超时,但这会引发异常 - 上述解决方案可让您控制行为而不会引发异常(即使您始终可以 try/catch
)
这是一个扩展方法,您可以使用它来显式观察未观察到时可能会失败的任务:
public static Task<T> AsObserved<T>(this Task<T> task)
{
task.ContinueWith(t => t.Exception);
return task;
}
用法示例:
var task = Task.Run(() => new EA.Repository()).AsObserved();