c# - 具有附加异常处理的异步任务<T>的超时

标签 c# asynchronous timeout interop

在我的项目中,我引用动态链接库中的类型和接口(interface)。 使用这个特定库时,我要做的第一件事是创建一个 EA.Repository 实例,它在库中定义,并作为进一步使用的入口点。 p>

实例化EA.Repository repository = new EA.Repository()在后台执行一些复杂的操作,我发现自己面临三种可能的结果:

  1. 实例化需要一些时间,但最终成功完成
  2. 抛出异常(立即或在一段时间后)
  3. 实例化永远阻塞(在这种情况下,我想取消并通知用户)

我能够使用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();
    }
}

(我知道,您可能已经在这里发现了很多问题。请耐心等待...建议将不胜感激!)

到目前为止,这种方法有效 - 我可以模拟“异常”和“超时”场景,并且获得了所需的行为。

但是,我发现了另一种边缘情况:假设实例化任务花费了足够长的时间,以致超时到期,然后引发异常。在这种情况下,我有时会得到一个 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)

关于c# - 具有附加异常处理的异步任务<T>的超时,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58676541/

相关文章:

c# - 与 ASP.NET Core 中的 FromBody 混淆

c# - 获取两次之间的时间,时间跨度为一小时

c# - 在生成服务器上编译异步时无效 token 'void'..await

javascript - 在 setTimeout 中将多个参数传递给 promise 解析

mysql - 防止 Mongrel/Mysql Errno::EPIPE 异常

c# - 将应用程序从 .net 4.0 降级到 3.5

c# - 如何通过 Google Drive .NET API v3 使用服务帐户访问 Team Drive

java - 在 Firebase Admin SDK 之上创建 REST API Controller

ruby-on-rails - Heroku 上的 Rails 应用程序中的随机超时异常

javascript - 类型错误 : $timeout is not a function