如果需要太长时间,请取消执行命令

时间:2016-01-13 10:30:47

标签: c# multithreading

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

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

用于完美工作的代码。但是在最后几分钟,当我尝试启动它时,我的程序似乎无所事事。在调试时按下暂停显示我已经"卡住"在上面的一行。
我的猜测是他们只是做了一些维护或其他什么,但这不是重点。
所以我觉得如果程序没有启动会告诉用户出了什么问题会很好。像

这样简单的东西
MessageBox.Show("Could not connect", "Connection Error");

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

4 个答案:

答案 0 :(得分:5)

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

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

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

    1. ......跳到其他地方?
    2. 好方法是使用TPL和异步模型。 这是一个包装任何Task的扩展方法,并在超时后过期。

      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您可以找到更多信息

答案 1 :(得分:3)

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

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。

答案 2 :(得分:2)

最简单的方法是,如果没有实现本机超时,如上所述,是一个单独的线程来加载它。虽然这听起来真的很脏,但它很简单(使用Rx):

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

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

答案 3 :(得分:-1)

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

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