【问题标题】:How to limit the execution time of a function in c sharp?如何限制c中函数的执行时间?
【发布时间】:2011-09-14 08:48:49
【问题描述】:

我有一个问题。我正在编写一个基准测试,我有一个功能可以在 2 秒内或约 5 分钟后完成(取决于输入数据)。如果它执行超过 3 秒,我想停止该功能......

我该怎么做?

非常感谢!

【问题讨论】:

  • 是的,它是重复的,基准测试以与任何其他线程停止相同的方式结束。

标签: c# execution-time time-limiting


【解决方案1】:

嗯...,我也有同样的问题,在阅读了这里的所有答案和参考的博客之后,我决定了,

它让我执行任何有时间限制的代码块,声明包装方法

    public static bool ExecuteWithTimeLimit(TimeSpan timeSpan, Action codeBlock)
    {
        try
        {
            Task task = Task.Factory.StartNew(() => codeBlock());
            task.Wait(timeSpan);
            return task.IsCompleted;
        }
        catch (AggregateException ae)
        {
            throw ae.InnerExceptions[0];
        }   
    }

并用它来包装任何这样的代码块

    // code here

    bool Completed = ExecuteWithTimeLimit(TimeSpan.FromMilliseconds(1000), () =>
    {
         //
         // Write your time bounded code here
         // 
    });

    //More code

【讨论】:

  • 据我了解,如果在 timeSpan 之后仍在运行,这不会停止 action codeBlock。在您的基准测试场景中,如果您有多个并行运行的代码,应该会极大地改变您的结果。
  • 我认为你需要写 Task task = new Task(codeBlock); task.Wait(timeSpan); task.Start(); return task.IsCompleted; 因为你的代码,你正在启动方法并告诉它等待 x 次。但实际上只是分配一个任务等待并启动任务是一种更好的方法。
  • 完美运行。
【解决方案2】:
private static int LongRunningMethod()
{
    var r = new Random();

    var randomNumber = r.Next(1, 10);

    var delayInMilliseconds = randomNumber * 1000;

    Task.Delay(delayInMilliseconds).Wait();

    return randomNumber;
}

var task = Task.Run(() =>
{
    return LongRunningMethod();
});

bool isCompletedSuccessfully = task.Wait(TimeSpan.FromMilliseconds(3000));

if (isCompletedSuccessfully)
{
    return task.Result;
}
else
{
    throw new TimeoutException("The function has taken longer than the maximum time allowed.");
}

它对我有用! 来源:https://jeremylindsayni.wordpress.com/2016/05/28/how-to-set-a-maximum-time-to-allow-a-c-function-to-run-for/

【讨论】:

    【解决方案3】:

    最好的方法是你的函数可以经常检查它的执行时间,以决定停止它需要很长时间。

    如果不是这种情况,则在单独的线程中运行该函数。在您的主线程中启动一个 3 秒计时器。当计时器结束时,使用Thread.Abort() 终止单独的线程(当然,除非函数已经结束)。请参阅函数文档中的示例代码和使用说明。

    【讨论】:

    • 这确实容易多了。如果你真的需要以不友好的方式杀死线程,使用线程是我知道的唯一方法。
    • 是的,这似乎是最简单、最直接的方法。谢谢
    【解决方案4】:

    在 C# 中停止函数的最佳方法是在函数中使用 return 关键字,但我怎么知道什么时候使用 return 关键字在中间停止函数,至少持续 3 秒后?来自System.DiagnosticsStopwatch 类就是答案。这个持续 2 秒到 5 分钟(取决于输入数据)的复杂函数在逻辑上使用了许多循环,甚至可能使用递归,所以我给你的解决方案是,在该函数的第一行代码中,创建一个 @ 的实例987654325@ 使用System.Diagnosticsnew 关键字,通过调用Stopwatch 类的Start() 函数启动它,并在每个循环和循环的开头,添加以下代码:

    if (stopwatch.ElapsedMilliseconds >= 3000) { 
         stopwatch.Stop();
         // or 
         stopwatch.Reset(); 
         return;
     } 
    

    (提示:你可以用手输入一次,按Ctrl+C复制,然后按Ctrl+V粘贴)。如果该函数使用递归,为了节省内存,请先创建 Stopwatch 全局实例,而不是将其创建为本地实例,如果它在代码开头没有运行,则启动它。您可以通过 Stopwatch 类的IsRunning 知道这一点。之后询问经过的时间是否超过 3 秒,如果是 (true) 则停止或重置秒表,并使用 return 关键字停止递归循环,如果您的函数持续很长时间,很好的开始函数时间到期主要是递归多于循环。这是。如您所见,它非常简单,我测试了这个解决方案,结果表明它有效!自己试试吧!

    【讨论】:

    • 最佳答案简单明了。你有很好的内容,但你应该考虑修改。
    【解决方案5】:

    您可以使用 fork/join 模式,在任务并行库中,这是通过 Task.WaitAll() 实现的

    using System.Threading.Tasks;
    
    void CutoffAfterThreeSeconds() {
    
        // start function on seperate thread
        CancellationTokenSource cts = new CancellationTokenSource();
        Task loop = Task.Factory.StartNew(() => Loop(cts.Token));
    
        // wait for max 3 seconds
        if(Task.WaitAll(new Task[]{loop}, 3000)){
           // Loop finished withion 3 seconds
        } else {
           // it did not finish within 3 seconds
           cts.Cancel();           
        }        
    }
    
    // this one takes forever
    void Loop() {
        while (!ct.IsCancellationRequested) {
            // your loop goes here
        }
        Console.WriteLine("Got Cancelled");
    }
    

    这将在单独的线程上启动另一个任务,然后等待 3000 毫秒以使其完成。如果它确实在超时内完成,则返回 true,否则返回 false,因此您可以使用它来决定下一步要做什么。

    您可以使用CancellationToken 与另一个线程进行通信,结果不再需要它,因此它可以优雅地停止。

    问候格特-扬

    【讨论】:

    • 好的,看起来简单易用:) 最后一个问题:如何以某种方式取消正在运行的 Loop()?
    • 取决于你在那里做什么......它是某种循环(你可以在其中检查取消令牌吗?)或者你是否调用了一些你不能调用的其他代码改变?这决定了策略。如果长时间运行的操作是您的代码,那么您可以查看 CancellationToken。如果是其他代码,那么您可能需要不那么友好并真正杀死线程。这可能会导致不一致的状态。我将使用取消令牌更新示例。
    • 您好,我认为它不够好。如果可以更改您正在调用的代码的实现,请查看stackoverflow.com/questions/7413612/…。如果您无法更改实现,那么这种方法也将不起作用。我将再次更改示例。
    • TPL 中实际上没有办法强制取消任务。我想最简单的方法实际上是检查你打电话的方法,看看你忙了多久,然后在那里取消。否则使用上述方法并使用取消令牌。
    • @gjvdkamp 如果循环时间少于 3000,您如何获得结果?
    【解决方案6】:

    在线程中运行此函数并在 3 秒后将其终止 检查此函数内部经过的时间(我认为它在那里循环)。

    【讨论】:

    • 这只会把函数变成一个检查循环,让它没有其他功能。也不能保证杀死线程。在退出函数之前它不会被杀死。
    【解决方案7】:

    使用带有高性能计数器的操作系统回调,然后杀死你的线程(如果存在)

    【讨论】:

    • 杀死托管线程的 API 方法是否确保即时(在下一次处理器迭代中)线程杀死?
    【解决方案8】:

    由于 C# 和 .net 框架不是实时环境,因此您甚至无法保证 3 秒的计数。即使你接近那个,你仍然需要调用

    if(timeSpan > TimeSpan.FromSeconds(3) then goto endindentifier; 在方法中的所有其他调用之前。

    所有这些都是错误的,所以不,据我所知,没有可靠的方法来做到这一点。

    虽然你可以试试这个解决方案

    https://web.archive.org/web/20140222210133/http://kossovsky.net/index.php/2009/07/csharp-how-to-limit-method-execution-time

    但我不会在 .net 应用程序中做这样的事情。

    【讨论】:

    • 嗨,最大。 Microsoft Windows 也不是实时操作系统
    • 您可以在使用 Win32 和互操作的 Windows 上使用带有高性能计数器的回调,然后
    • @Artur,我之所以说 .net 而不是 windows,是因为 C# 也可以通过 Mono 在基于 Unix 的操作系统上执行,我不确定是否所有支持 Mono 的操作系统不是实时的。组装一个实时系统可能是可能的,但 Mono 很可能永远不会是实时的,就像 .Net 一样,只是因为目前不需要它。
    • 我们说的是 3 秒和 5 分钟之间的差异。不完全是 RTOS 要求!
    • @Serge - 我有点让这个要求。飞过。 Kossovsky 先生在他的博客中展示的 Limex 方法很好。
    【解决方案9】:

    可以在单独的线程中执行一个函数,并用Thread.Join(millisecondsTimeout)限制它的执行:

    using System.Threading;
    
    Thread workThread = new Thread(DoFunc);
    workThread.Start(param);
    
    if (!workThread.Join(3000))
    {
        // DoFunc() took longer than 3 seconds. Thread was aborted
    }
    
    private void DoFunc(object param)
    {
        // do some long work
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-04-23
      • 2011-08-29
      • 1970-01-01
      • 2010-11-13
      • 2021-07-12
      相关资源
      最近更新 更多