Подтвердить что ты не робот

Надежно остановите System.Threading.Timer?

Хорошо, я много искал для решения этого. Я ищу чистый и простой способ предотвратить метод callback метода System.Threading.Timer после того, как я его остановил.

Кажется, я ничего не могу найти, и это привело меня к тому, чтобы прибегнуть к ужасной нитевой нить .sleep-thread.abort combo дрожь.

Можно ли это сделать с помощью блокировки? Пожалуйста, помогите мне найти хороший способ сделать это. Благодаря

4b9b3361

Ответ 1

like Конрад Фрикс предложил вам использовать класс System.Timers.Timer, например:

private System.Timers.Timer _timer = new System.Timers.Timer();
private volatile bool _requestStop = false;

public constructor()
{
    _timer.Interval = 100;
    _timer.Elapsed += OnTimerElapsed;
    _timer.AutoReset = false;
    _timer.Start();
}

private void OnTimerElapsed(object sender, System.Timers.ElapsedEventArgs e)
{
    // do work....
    if (!_requestStop)
    {
        _timer.Start();//restart the timer
    }
}

private void Stop()
{
    _requestStop = true;
    _timer.Stop();
}

private void Start()
{
    _requestStop = false;
    _timer.Start();
}

Ответ 2

Более простым решением может быть установка Timer никогда не возобновлять; метод Timer.Change может принимать значения для dueTime и period, которые указывают, что таймер никогда не перезапускается:

this.Timer.Change(Timeout.Infinite, Timeout.Infinite);

В то время как переход на использование System.Timers.Timer может быть "лучшим" решением, всегда будут моменты, когда это непрактично; достаточно использовать Timeout.Infinite.

Ответ 3

Для System.Threading.Timer можно сделать следующее (также будет защищать метод обратного вызова от работы с установленным таймером - ObjectDisposedException):

class TimerHelper : IDisposable
{
    private System.Threading.Timer _timer;
    private readonly object _threadLock = new object();

    public event Action<Timer,object> TimerEvent;

    public void Start(TimeSpan timerInterval, bool triggerAtStart = false,
        object state = null)
    {
        Stop();
        _timer = new System.Threading.Timer(Timer_Elapsed, state,
            System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);

        if (triggerAtStart)
        {
            _timer.Change(TimeSpan.FromTicks(0), timerInterval);
        }
        else
        {
            _timer.Change(timerInterval, timerInterval);
        }
    }

    public void Stop(TimeSpan timeout = TimeSpan.FromMinutes(2))
    {
        // Wait for timer queue to be emptied, before we continue
        // (Timer threads should have left the callback method given)
        // - http://woowaabob.blogspot.dk/2010/05/properly-disposing-systemthreadingtimer.html
        // - http://blogs.msdn.com/b/danielvl/archive/2011/02/18/disposing-system-threading-timer.aspx
        lock (_threadLock)
        {
            if (_timer != null)
            {
                ManualResetEvent waitHandle = new ManualResetEvent(false)
                if (_timer.Dispose(waitHandle))
                {
                   // Timer has not been disposed by someone else
                   if (!waitHandle.WaitOne(timeout))
                      throw new TimeoutException("Timeout waiting for timer to stop");
                }
                waitHandle.Close();   // Only close if Dispose has completed succesful
                _timer = null;
            }
        }
    }

    public void Dispose()
    {
        Stop();
        TimerEvent = null;
    }

    void Timer_Elapsed(object state)
    {
        // Ensure that we don't have multiple timers active at the same time
        // - Also prevents ObjectDisposedException when using Timer-object
        //   inside this method
        // - Maybe consider to use _timer.Change(interval, Timeout.Infinite)
        //   (AutoReset = false)
        if (Monitor.TryEnter(_threadLock))
        {
            try
            {
                if (_timer==null)
                    return;

                Action<Timer, object> timerEvent = TimerEvent;
                if (timerEvent != null)
                {
                    timerEvent(_timer, state);
                }
            }
            finally
            {
                Monitor.Exit(_threadLock);
            }
        }
    }
}

Вот как это можно использовать:

void StartTimer()
{
    TimerHelper _timerHelper = new TimerHelper();
    _timerHelper.TimerEvent += (timer,state) => Timer_Elapsed();
    _timerHelper.Start(TimeSpan.FromSeconds(5));
    System.Threading.Sleep(TimeSpan.FromSeconds(12));
    _timerHelper.Stop();
}

void Timer_Elapsed()
{
   // Do what you want to do
}

Ответ 4

Для чего это стоит, мы используем этот шаблон совсем немного:

// set up timer
Timer timer = new Timer(...);
...

// stop timer
timer.Dispose();
timer = null;
...

// timer callback
{
  if (timer != null)
  {
    ..
  }
}

Ответ 5

Документы MSDN предлагают использовать метод Dispose(WaitHandle), чтобы остановить таймер +, чтобы быть информированным, когда обратные вызовы больше не будут вызываться.

Ответ 6

Мне кажется, это правильный путь: Просто позвоните dispose, когда закончите с таймером. Это остановит таймер и предотвратит будущие запланированные вызовы.

См. пример ниже.

class Program
{
    static void Main(string[] args)
    {
        WriteOneEverySecond w = new WriteOneEverySecond();
        w.ScheduleInBackground();
        Console.ReadKey();
        w.StopTimer();
        Console.ReadKey();
    }
}

class WriteOneEverySecond
{
    private Timer myTimer;

    public void StopTimer()
    {
        myTimer.Dispose();
        myTimer = null;
    }

    public void ScheduleInBackground()
    {
        myTimer = new Timer(RunJob, null, 1000, 1000);
    }

    public void RunJob(object state)
    {
        Console.WriteLine("Timer Fired at: " + DateTime.Now);
    }
}

Ответ 7

Этот ответ относится к System.Threading.Timer

Я прочитал много глупостей о том, как синхронизировать удаление System.Threading.Timer по всей сети. Поэтому, почему я публикую это, чтобы немного исправить ситуацию. Не стесняйтесь сказать мне/позвонить мне, если что-то я пишу неправильно, -)

Ловушки

По-моему, эти подводные камни:

  • Timer.Dispose(WaitHandle) может возвращать false. Он делает это, если он уже был удален (я должен был смотреть на исходный код). В этом случае он не установит WaitHandle - так что не ждите его!
  • не обрабатывает тайм-аут WaitHandle. Серьезно - чего вы ждете, если вас не интересует таймаут?
  • Concurrency выдайте, как указано здесь, в msdn, где ObjectDisposedException может произойти во время (не после) удаления.
  • Timer.Dispose(WaitHandle) работает неправильно - Slim waithandles, или нет, как и следовало ожидать. Например, следующее не работает (оно блокируется навсегда):
 using(var manualResetEventSlim = new ManualResetEventSlim)
 {
     timer.Dispose(manualResetEventSlim.WaitHandle);
     manualResetEventSlim.Wait();
 }

Решение

Ну, название немного "смелое", я думаю, но ниже моя попытка разобраться с проблемой - обертка, которая обрабатывает двойное удаление, таймауты и ObjectDisposedException. Он не предоставляет все методы на Timer, хотя, но не стесняйтесь их добавлять.

internal class Timer
{
    private readonly TimeSpan _disposalTimeout;

    private readonly System.Threading.Timer _timer;

    private bool _disposeEnded;

    public Timer(TimeSpan disposalTimeout)
    {
        _disposalTimeout = disposalTimeout;
        _timer = new System.Threading.Timer(HandleTimerElapsed);
    }

    public event Signal Elapsed;

    public void TriggerOnceIn(TimeSpan time)
    {
        try
        {
            _timer.Change(time, Timeout.InfiniteTimeSpan);
        }
        catch (ObjectDisposedException)
        {
            // race condition with Dispose can cause trigger to be called when underlying
            // timer is being disposed - and a change will fail in this case.
            // see 
            // https://msdn.microsoft.com/en-us/library/b97tkt95(v=vs.110).aspx#Anchor_2
            if (_disposeEnded)
            {
                // we still want to throw the exception in case someone really tries
                // to change the timer after disposal has finished
                // of course there a slight race condition here where we might not
                // throw even though disposal is already done.
                // since the offending code would most likely already be "failing"
                // unreliably i personally can live with increasing the
                // "unreliable failure" time-window slightly
                throw;
            }
        }
    }

    private void HandleTimerElapsed(object state)
    {
        Elapsed.SafeInvoke();
    }

    public void Dispose()
    {
        using (var waitHandle = new ManualResetEvent(false))
        {
            // returns false on second dispose
            if (_timer.Dispose(waitHandle))
            {
                if (!waitHandle.WaitOne(_disposalTimeout))
                {
                    throw new TimeoutException(
                        "Timeout waiting for timer to stop. (...)");
                }
                _disposeEnded = true;
            }
        }
    }
}

Ответ 8

Возможно, вы должны сделать обратное. Используйте system.timers.timer, установите для параметра AutoReset значение false и запустите его, когда хотите

Ответ 9

Вы не можете гарантировать, что ваш код, который должен был остановить таймер, будет выполняться перед вызовом события таймера. Например, предположим, что в момент времени 0 вы инициализировали таймер для вызова события, когда наступает момент 5. Затем в момент времени 3 вы решили, что вам больше не нужен звонок. И называется метод, который вы хотите написать здесь. Тогда, в то время как метод был JIT-ted, приходит момент времени 4, и ОС решает, что ваша нить исчерпала свой временной срез и переключатель. И таймер будет вызывать событие независимо от того, как вы пытаетесь - у вашего кода просто не будет возможности работать в худшем случае.

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

Ответ 10

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

public class InvalidWaitHandle : WaitHandle
{
    public IntPtr Handle
    {
        get { return InvalidHandle; }
        set { throw new InvalidOperationException(); }
    }
}

Мгновенный таймер:

_t = new Timer(DisplayTimerCallback, TBlockTimerDisplay, 0, 1000);

Затем внутри метода обратного вызова:

if (_secondsElapsed > 80)
{
    _t.Dispose(new InvalidWaitHandle());
}

Ответ 11

Существует ссылка MSDN, как правильно установить стоп-таймер. Используйте метод ControlThreadProc() с событием HandleElapsed(object sender, ElapsedEventArgs e), синхронизированным переменной syncPoint static class. Прокомментируйте Thread.Sleep(testRunsFor); на ControlThreadProc(), если он не подходит (возможно). Ключ заключается в использовании статической переменной и атомной операции типа Interlocked.CompareExchange для условных операторов.

Ссылка: Timer.Stop Method