繁体   English   中英

如果花费的时间太长,则取消执行命令

[英]Cancel execution of command if it takes too long

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

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

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

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

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

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

  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
}

在这里您可以找到更多信息

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

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 你可以抛出异常(任务没有及时完成。)

如果您有一个不返回任何内容的 Action,它会更简单:

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

其中action是一些Action或 lambda。

如您所述,如果未实现本机超时,最简单的方法是使用一个单独的线程来加载它。 虽然这听起来很脏,但它就像(使用 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

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

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

请参阅 Microsoft 的此文档

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.");
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM