【问题标题】:Cancel execution of command if it takes too long如果时间过长,取消命令的执行
【发布时间】:2016-01-13 10:30:46
【问题描述】:

我目前正在开发一个使用来自不同部门的程序集的软件。
我从这个程序集中调用一个方法,如下所示:

using (var connection = (IConnection) Factory.GetObject(typeof (IConnection)))

以前的代码可以正常工作。但是在最后几分钟,当我尝试启动它时,我的程序似乎什么也没做。在调试时按下暂停键显示它“卡在”上面的行。
我的猜测是他们只是在做一些维护或其他事情,但这不是重点。
所以我认为如果程序没有启动,告诉用户出了什么问题会很好。像这样简单的东西

MessageBox.Show("Could not connect", "Connection Error");

然后关闭程序。我的问题是:
如何在设定的时间后终止命令的执行并跳转到其他地方?
我的猜测是将它移动到一个单独的线程,然后把调用线程休眠几秒钟,之后如果尚未完成,它将处理额外的线程。但这对我来说似乎真的很脏,必须有更好的方法。

【问题讨论】:

  • 如果你的IConnectionSqlConnection,你可以使用它提供的默认连接超时。
  • @Kilanny 尽管用法非常相似,但它不是 SqlConnection,似乎没有人会费心实现超时。
  • 否则使用Timer 来检查执行,超时时应该终止作业
  • using 声明是为了在中止线程后进行整理,至少在理论上...
  • @slawekin using 块用于在离开块时自动处置指定对象。但这从未发生过,因为我一开始就没有得到对象。

标签: c# multithreading


【解决方案1】:

您的问题可以分为两部分:

  1. 如何终止命令的执行?

唯一的方法是中止线程。 但不要这样做。没有保证和安全的方法。有像 Thread.Interrupt 和 Thread.Abort 这样的方法可以唤醒线程。但它们只有在线程处于 WaitSleepJoin 状态并且挂在托管代码中时才会起作用。

好像你已经知道了。但是再一次,如果程序集中的某些东西无限地挂起代码的执行,那么线程可能已经“消失”了。所以你应该关闭程序是对的。

  1. ...跳到别的地方?

好的方法是使用 TPL 和异步模型。 这是一个扩展方法来包装任何任务并在超时后过期。

public static async Task TimeoutAfter(this Task task, int millisecondsTimeout)
{
    if (task == await Task.WhenAny(task, Task.Delay(millisecondsTimeout)))
        await task;
    else
        throw new TimeoutException();
}

那就用吧

try
{
    using (var result = await Task.Run(() => (IConnection)Factory.GetObject(typeof(IConnection))).TimeoutAfter(1000))
    {
        ...
    }
}
catch (TimeoutException ex)
{
    //timeout
}

Here你可以找到更多信息

【讨论】:

  • 详细的答案给出了更详细的指示。我喜欢它。谢谢。
【解决方案2】:

一种无需额外库或扩展方法的简单方法:

using ( var task = new Task<IConnection>( () => Factory.GetObject( typeof( IConnection ) ) ) )
{
    task.Start();

    if( !task.Wait( timeoutMilliseconds ) )
    {
        throw new TimeoutException();
    }

    IConnection result = task.Result;
}

Task.Wait 做你想做的事,因为如果它返回 false(任务没有及时完成),你可以抛出异常。

如果你有一个不返回任何东西的动作,那就更简单了:

if ( !Task.Run( action ).Wait( timeoutMilliseconds ) )
{
    throw new TimeoutException();
}

action 是一些 Action 或 lambda。

【讨论】:

    【解决方案3】:

    如果没有实现本机超时,那么最简单的方法是使用单独的线程来加载它。虽然这听起来会很脏,但它就像(使用 Rx)一样简单:

    Task<IConnection> connectionTask = Observable.Start(() => Factory.GetObject(typeof (IConnection)), Scheduler.Default).Timeout(TimeSpan.FromSeconds(20)).ToTask());
    using (var connection = connectionTask.Result)
    {
        ...
    }
    

    如果您不希望它在线程池上运行,您可以调整Scheduler。如果 Factory.GetObject 调用时间超过 20 秒,它将抛出 TimeoutException

    【讨论】:

    • 同样的想法,但更流畅。非常感谢。我用try-catch 包围它,但是当我尝试调试时,它说它永远不会到达我放置在catch 中的断点。我觉得很奇怪,我明天再看看这个。
    【解决方案4】:

    您可以使用 CancellationTokenSource 设置操作超时

    var timeoutCts = new CancellationTokenSource();
    try
    {
        timeoutCts.CancelAfter(300000); // Cancel after 5 minutes
        // ... run your long term operation
    }
    catch (OperationCanceledException ex)
    {
        // Handle the timeout
    }
    

    【讨论】:

      【解决方案5】:

      请参阅来自 Microsoft 的 this documentation

      using System;
      using System.Threading.Tasks;
      
      public class Example
      {
          public static void Main()
          {
              Task t = Task.Run(() =>
              {
                  Random rnd = new Random();
                  long sum = 0;
                  int n = 5000000;
                  for (int ctr = 1; ctr <= n; ctr++)
                  {
                      int number = rnd.Next(0, 101);
                      sum += number;
                  }
                  Console.WriteLine("Total:   {0:N0}", sum);
                  Console.WriteLine("Mean:    {0:N2}", sum / n);
                  Console.WriteLine("N:       {0:N0}", n);
              });
              TimeSpan ts = TimeSpan.FromMilliseconds(150);
              if (!t.Wait(ts))
                  Console.WriteLine("The timeout interval elapsed.");
          }
      }
      

      【讨论】:

      • 链接并从链接复制粘贴代码并不是真正的答案,下次您应该将链接作为问题的评论发布
      • 我认为任何人都可以轻松地复制代码并完成它,而不是打开包含太多信息的链接。不过没关系。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多