«Watch Dog» или песочница в c # - PullRequest
       31

«Watch Dog» или песочница в c #

2 голосов
/ 04 августа 2009

У меня есть функция, которую я хочу разрешить запускать в течение заданного промежутка времени, а затем, если она не завершилась сама по себе, отменить. Каков наилучший способ сделать это?

Лучшее, о чем я подумал, это запустить его в другом потоке, подождать с таймаутом, пока он не умрет, и затем использовать Thread.Abort(), чтобы убить его (это может не сработать, если функция имеет неправильный тип catch блок). Другим вариантом (который я не знаю, как заставить работать) будет какой-то упреждающий таймер с броском в нем.

Есть ли лучший способ? Какая-то простая система песочницы?


Редактировать: функция, которую я собираюсь запустить, не имеет никакой системы, чтобы проверить, должна ли она быть отменена, и я не могу (как в не должно ) добавить Это. Кроме того, это своего рода тестовый жгут, поэтому условие, при котором я буду убивать функцию, заключается в том, что она работает плохо. В этом случае я не могу рассчитывать на то, что он что-то делает правильно.

Ответы [ 8 ]

5 голосов
/ 04 августа 2009

Это может быть немного излишним для чего-то подобного, но вы можете посмотреть, хотите ли вы загрузить все, что размещает этот поток, в домен приложений. AppDomain по сути является песочницей .NET.

Если поток уходит в сорняки, вы можете просто уничтожить домен приложений.

2 голосов
/ 04 августа 2009

То, что вы хотите реализовать, это BackgroundWorker. BackgroundWorker может выполнять фоновую операцию и предоставляет механизмы для уведомления этой фоновой операции об отмене.

Фоновая операция должна периодически проверять свойство Cancel переданного в DoWorkEventArgs экземпляра и грациозно убивать себя, если значение равно true.

Установка свойства Cancel в экземпляре DoWorkEventArgs может быть достигнута путем вызова метода CancelAsync в инициирующем экземпляре BackgroundWorker.

Хороший пример есть в документации MSDN . Также смотрите вклады сообщества и соответствующие ссылки внизу этой страницы.


Вы также можете найти этот пост интересным, он использует Thread.Abort, хотя я бы порекомендовал вам избегать ... C # Установить время ожидания метода с помощью Generics

1 голос
/ 04 августа 2009

Если вы не можете легко прервать процесс своей функции, вам, возможно, придется использовать таймер для прерывания текущего потока (это позволяет избежать выполнения вашей функции в другом потоке). Как только текущий поток прерывается, вы отменяете этот прерывание с помощью Thread.ResetAbort () и можете выполнять другие шаги вашей программы.

Таким образом, вы можете использовать класс, подобный этому

using System.Threading;
using System.Timers;

namespace tools
{
    public class ThreadAbortTimer
    {
        public ThreadAbortTimer(int timeout)
        {
            _CurrentThread = Thread.CurrentThread;
            _Timer = new System.Timers.Timer();
            _Timer.Elapsed += _Timer_Elapsed;
            _Timer.Interval = timeout;
            _Timer.Enable = true;
        }

        /// <summary>
        /// catch the timeout : if the current thread is still valid, it is aborted
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void _Timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            lock (typeof(ThreadAbortTimer))
            {
                if (_CurrentThread != null)
                {
                    _CurrentThread.Abort();
                    _CurrentThread = null;
                }
            }
        }

        /// <summary>
        /// timer that will check if the process lasts less than 30 seconds
        /// </summary>
        private readonly System.Timers.Timer _Timer;

        /// <summary>
        /// current thread to abort if the process is longer than 30 sec
        /// </summary>
        private Thread _CurrentThread;

        /// <summary>
        /// stop the timer
        /// </summary>
        public void Disable()
        {
            lock (typeof(ThreadAbortTimer))
            {
                _Timer.Enabled = false;
                _CurrentThread = null;
            }
        }

        /// <summary>
        /// dispose the timer
        /// </summary>
        public void Dispose()
        {
            _Timer.Dispose();
        }
    }
}

и затем вы можете использовать его так:

   using (var timer = new ThreadAbortTimer(timeout))
    {
        try
        {
            // the process you want to timeout
        }
        catch
        {
            timer.Disable();
            Thread.ResetAbort();
        }
    }
1 голос
/ 04 августа 2009

Возможно, я не правильно понимаю проблему, но почему бы не поместить логику в саму функцию? Пример (C #):

public void someLongFunction()
{
    DateTime start = DateTime.Now;
    while (DateTime.Now <= start.AddMinutes(5)) // assuming 5 mins run time
    {
         // long processing code here
         if (DateTime.Now > start.AddMinutes(5))
             break;
         // more long processing code
    }
    // thread clean up etc. here
}
1 голос
/ 04 августа 2009

Почему бы функции не выйти? Какой результат это дает?

Моей первой попыткой было бы сделать код достаточно плотным и обрабатывать все «зависшие» случаи, чтобы вам не требовался сторожевой таймер - если вы ожидаете освобождения ресурсов, установите время ожидания на ожидание и обрабатывать исключения соответствующим образом.

Если не считать этого, я бы попытался создать сторожевой таймер вне процесса, выполняя Process.Kill по мере необходимости. Это более эффективно, чем Thread.Abort - если вы идете по пути жестокого решения, почему бы не пройти весь путь?

1 голос
/ 04 августа 2009

Сделайте все от вас зависящее, чтобы найти разумное решение этой проблемы. Исключением является не управление потоком, и прерывание потока намного хуже. Тем не менее, вы можете запустить его в потоке и прервать его после истечения времени ожидания.

Редактировать: Вот строгое, но правильное объяснение того, насколько я призываю вас найти другое решение: http://tdanecker.blogspot.com/2007/08/do-never-ever-use-threadabort.html

0 голосов
/ 04 августа 2009

Я знаю, что это может показаться излишним, но технически это может работать так, как вы хотите. Разделите код, над которым вы работаете в 2. Первая часть будет менеджером, а вторая - исполнителем.

Executor (в вашем случае) может быть простым приложением командной строки с любой функцией, которую вы хотите протестировать. На этом этапе приложение менеджера вызывает приложение командной строки с помощью класса Process (System.Diagnostics). Одним из методов класса процесса является WaitForExit, который будет ожидать выхода вашего консольного приложения. Но если приложение не закрывается, вы можете указать время ожидания, прежде чем принудительно завершать работу приложения.

Используя это, вы сможете получить то, что хотите

0 голосов
/ 04 августа 2009

Вы можете использовать Thread.Join (), чтобы ограничить выполнение потока.

...