Как ограничить время выполнения функции в диез? - PullRequest
13 голосов
/ 14 сентября 2011

У меня проблема. Я пишу тест, и у меня есть функция, которая выполняется через 2 секунды или через ~ 5 минут (в зависимости от входных данных). И я хотел бы остановить эту функцию, если она выполняется более 3 секунд ...

Как я могу это сделать?

Большое спасибо!

Ответы [ 8 ]

50 голосов
/ 11 мая 2012

Ну ... у меня был тот же вопрос, и после прочтения всех ответов здесь и упомянутых блогов, я согласился на это,

Позволяет мне выполнить любой блок кода с ограничением по времени. Объявить метод оболочки

.
    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
5 голосов
/ 14 сентября 2011

Наилучшим способом было бы, чтобы ваша функция могла достаточно часто проверять время выполнения, чтобы решить, остановить ли это, это занимает слишком много времени.

Если это не так, тогда запустите функцию в отдельном потоке. В вашем основном потоке запустите 3 секундный таймер. Когда таймер истекает, убейте отдельный поток, используя Thread.Abort () (конечно, если функция не завершена). См. Пример кода и рекомендации по использованию в документации по функции.

3 голосов
/ 04 марта 2013

Лучший способ в C # остановить функцию в середине - это ключевое слово return в функции, но как узнать, когда использовать ключевое слово return, чтобы остановить функцию в середине, после того, как она длится не менее 3 секунд?Stopwatch класс из System.Diagnostics является ответом.Эта сложная функция, которая длится от 2 секунд до 5 минут (в зависимости от входных данных), логически использует много циклов и, возможно, даже рекурсию, поэтому мое решение для вас заключается в том, чтобы в первой строке кода этой функции создать экземпляр Stopwatch используя System.Diagnostics с ключевым словом new, запустите его, вызвав функцию Start() класса Stopwatch, и в начале каждого цикла и цикла добавьте следующий код:

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

(совет: вы можете набрать его руками, скопировать Ctrl + C, а затем просто вставить Ctrl + V).Если эта функция использует рекурсию, для экономии памяти создайте глобальный экземпляр секундомера вместо того, чтобы сначала создавать его как локальный экземпляр, и запустите его, если он не запускается в начале кода.Вы можете узнать это с помощью IsRunning класса Секундомер.После этого спросите, прошло ли более 3 секунд, и если да (true), остановите или сбросьте секундомер, и используйте ключевое слово return, чтобы остановить цикл рекурсии, очень хороший старт в функции, если ваша функция работает долгоВремя в основном за счет рекурсии больше, чем зацикливание.Что это.Как видите, это очень просто, и я протестировал это решение, и результаты показали, что оно работает!Попробуйте сами!

3 голосов
/ 14 сентября 2011

Вы можете использовать шаблон 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 , чтобы сообщить другому потоку, что его результат больше не нужен, поэтому он может корректно остановиться.

С уважением, Герт-Ян

1 голос
/ 21 марта 2019
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/

0 голосов
/ 14 сентября 2011

Используйте обратные вызовы ОС с высокопроизводительным счетчиком производительности, а затем уничтожьте ваш поток, если он существует

0 голосов
/ 14 сентября 2011

Поскольку C # и .net framework не являются средами реального времени, вы не можете гарантировать даже количество секунд 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.

0 голосов
/ 14 сентября 2011

Запустите эту функцию в потоке и убейте ее через 3 секунды или проверьте истекшее время внутри этой функции (я думаю, что это цикл).

...