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

.NET, событие каждую минуту (в минуту). Является ли таймер лучшим вариантом?

Я хочу делать вещи каждую минуту в минуту (по часам) в приложении windows forms с помощью С#. Мне просто интересно, как лучше всего это сделать?

Я мог бы использовать таймер и установить его интервал до 60000, но чтобы заставить его работать на минуту, я должен был бы включить его в минуту точно, не очень жизнеспособный.

Я мог бы использовать таймер и установить его интервал до 1000. Затем в течение своего события тика я мог бы проверить текущую минуту часов на переменную, которую я установил, если минута изменилась, тогда запустите мой код. Меня это беспокоит, потому что я делаю свой компьютер каждые полчаса, чтобы выполнять работу каждые 1 минуту. Неужели это уродливо?

Я использую формы Windows и .Net 2.0, поэтому не хочу использовать DispatchTimer, который поставляется с .Net 3.5

Это должна быть довольно распространенная проблема. У кого-нибудь из вас лучший способ сделать это?

4b9b3361

Ответ 1

Основываясь на ответе от aquinas, который может дрейфовать и который не галочет точно в минуту только в течение одной секунды минуты:

static System.Timers.Timer t;

static void Main(string[] args)
{
    t = new System.Timers.Timer();
    t.AutoReset = false;
    t.Elapsed += new System.Timers.ElapsedEventHandler(t_Elapsed);
    t.Interval = GetInterval();
    t.Start();
    Console.ReadLine();
}

static double GetInterval()
{
    DateTime now = DateTime.Now;
    return ((60 - now.Second) * 1000 - now.Millisecond);
}

static void t_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
    Console.WriteLine(DateTime.Now.ToString("o"));
    t.Interval = GetInterval();
    t.Start();
}

На моем боксе этот код постоянно цепляется в пределах .02 с каждой минуты:

2010-01-15T16:42:00.0040001-05:00
2010-01-15T16:43:00.0014318-05:00
2010-01-15T16:44:00.0128643-05:00
2010-01-15T16:45:00.0132961-05:00

Ответ 2

Как насчет:

int startin = 60 - DateTime.Now.Second;
var t = new System.Threading.Timer(o => Console.WriteLine("Hello"), 
     null, startin * 1000, 60000);

Ответ 3

Создание Timer, который срабатывает каждые 1 секунду (и обычно ничего не делает, кроме простой проверки) добавит незначительные накладные расходы на ваш приложение.

Просто сравните значение Environment.TickCount или DateTime.Now до последнего сохраненного времени (предыдущий "минутный тик" ), и вы должны иметь достаточно точное решение. Разрешение этих двух значений времени составляет около 15 мс, что должно быть достаточным для ваших целей.

Обратите внимание, что интервал управления Timer не гарантированно будет таким точным или даже где-либо сейчас, поскольку он работает в цикле сообщений Windows, который связан с отзывчивостью пользовательского интерфейса. Никогда не полагайтесь на него даже для умеренно точного времени - хотя он достаточно хорош для стрельбы повторяющимися событиями, где вы можете проверить время, используя более чувствительный метод, такой как один из приведенных выше.

Ответ 4

Вы можете прикрепить это с помощью реактивных расширений, которые позаботятся о многих проблемах, связанных с таймером (изменения часов, спящий режим и т.д.). Используйте пакет Nuget Rx-Main и код, подобный этому:

Action work = () => Console.WriteLine(DateTime.Now.ToLongTimeString());

Scheduler.Default.Schedule(
    // start in so many seconds
    TimeSpan.FromSeconds(60 - DateTime.Now.Second), 
    // then run every minute
    () => Scheduler.Default.SchedulePeriodic(TimeSpan.FromMinutes(1), work));               

Console.WriteLine("Press return.");
Console.ReadLine();

Читайте здесь (ищите "Представление ISchedulerPeriodic" ), чтобы увидеть все проблемы, о которых он заботится: http://blogs.msdn.com/b/rxteam/archive/2012/06/20/reactive-extensions-v2-0-release-candidate-available-now.aspx

Ответ 5

Я написал этот класс с помощью WPF DispatcherTimer, но вы можете поменять диспетчер на любой таймер, который поддерживает изменение при его пробуждении из состояния сна.

Класс строится с фиксированным шагом времени и запускает Start/Stop/ Reset, Start/Stop/Start работает как операция возобновления. В этом отношении таймер похож на секундомер.

Реализация часов будет просто создавать класс с интервалом в 1 секунду и прослушивать событие. Будьте осторожны, хотя это часы реального времени, если событие тика занимает больше времени, чем интервал до конца, вы заметите, что часы будут пытаться догнать в реальном времени, это вызовет повышение количества событий тика.

public class FixedStepDispatcherTimer
{
    /// <summary>
    /// Occurs when the timer interval has elapsed.
    /// </summary>
    public event EventHandler Tick;

    DispatcherTimer timer;

    public bool IsRunning { get { return timer.IsEnabled; } }

    long step, nextTick, n;

    public TimeSpan Elapsed { get { return new TimeSpan(n * step); } }

    public FixedStepDispatcherTimer(TimeSpan interval)
    {
        if (interval < TimeSpan.Zero)
        {
            throw new ArgumentOutOfRangeException("interval");
        }
        this.timer = new DispatcherTimer();
        this.timer.Tick += new EventHandler(OnTimerTick);
        this.step = interval.Ticks;
    }

    TimeSpan GetTimerInterval()
    {
        var interval = nextTick - DateTime.Now.Ticks;
        if (interval > 0)
        {
            return new TimeSpan(interval);
        }
        return TimeSpan.Zero; // yield
    }

    void OnTimerTick(object sender, EventArgs e)
    {
        if (DateTime.Now.Ticks >= nextTick)
        {
            n++;
            if (Tick != null)
            {
                Tick(this, EventArgs.Empty);
            }
            nextTick += step;
        }
        var interval = GetTimerInterval();
        Trace.WriteLine(interval);
        timer.Interval = interval;
    }

    public void Reset()
    {
        n = 0;
        nextTick = 0;
    }

    public void Start()
    {
        var now = DateTime.Now.Ticks;
        nextTick = now + (step - (nextTick % step));
        timer.Interval = GetTimerInterval();
        timer.Start();
    }

    public void Stop()
    {
        timer.Stop();
        nextTick = DateTime.Now.Ticks % step;
    }
}

Ответ 6

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

Ответ 7

Как насчет Quartz.NET? Я думаю, что это хорошая структура для выполнения временных действий.

Ответ 8

Создайте метод или поместите этот код, где вы хотите запустить таймер:

 int time = 60 - DateTime.Now.Second; // Gets seconds to next minute
        refreshTimer.Interval = time * 1000;
        refreshTimer.Start();

И затем в вашем тике установите интервал до 60000:

  private void refreshTimer_Tick(object sender, EventArgs e)
    {
        refreshTimer.Interval = 60000; // Sets interval to 60 seconds
        // Insert Refresh logic
    }

Ответ 9

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

using System;
using System.Reactive.Linq;
namespace ConsoleApplicationExample
{
    class Program
    {
        static void Main()
        {
            Observable.Interval(TimeSpan.FromMinutes(1))
            .Subscribe(_ =>
            {                   
                Console.WriteLine(DateTime.Now.ToString());
            });
            Console.WriteLine(DateTime.Now.ToString()); 
            Console.ReadLine();
        }
    }
}

Ответ 10

Вы можете настроить два таймера. Начальный таймер с коротким интервалом времени (возможно, срабатывает каждую секунду, но зависит от того, как будет срабатывать второй таймер в минуту).

Вы запускаете таймер с коротким интервалом только до тех пор, пока не будет достигнуто желаемое время начала основного интервала. Как только начальное время достигнуто, можно активировать второй таймер основного интервала, а таймер с коротким интервалом можно отключить.

void StartTimer()
{

  shortIntervalTimer.Interval = 1000;
  mainIntervalTimer.Interval = 60000; 

  shortIntervalTimer.Tick += 
    new System.EventHandler(this.shortIntervalTimer_Tick);
  mainIntervalTimer.Tick += 
    new System.EventHandler(mainIntervalTimer_Tick);

  shortIntervalTimer.Start();

}

private void shortIntervalTimer_Tick(object sender, System.EventArgs e)
{
  if (DateTime.Now.Second == 0)
    {
      mainIntervalTimer.Start();
      shortIntervalTimer.Stop();
    }
}

private void mainIntervalTimer_Tick(object sender, System.EventArgs e)
{
  // do what you need here //
}

Ответ 11

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

Это имеет преимущество изменения таймаута, чтобы он не дрейфовал.

int timeout = 0;

while (true)  {
  timeout = (60 - DateTime.Now.Seconds) * 1000 - DateTime.Now.Millisecond;
  Thread.Sleep(timeout);

  // do your stuff here
}

Ответ 12

Использовать таймер, установленный для запуска каждую секунду (или миллисекунду, независимо от вашего порога точности), а затем закодировать метод для запуска вашей функции, если и только если текущее время находится в пределах этого порога, прошедшего через точку "в минуту".

Ответ 13

То, что я использую для запланированных задач, - это System.Threading.Timer(System.Threading.TimerCallback, object, int, int) с обратным вызовом, установленным для кода, который я хочу выполнить, на основе интервала, который предоставляется в миллисекунды для значения периода.

Ответ 14

Как насчет сочетания ответов aquinas и "опроса": (извинения за смесь языков)

def waitForNearlyAMinute:
    secsNow = DateTime.Now.Second;
    waitFor = 55 - secsNow;
    setupTimer(waitFor, pollForMinuteEdge)

def pollForMinuteEdge:
    if (DateTime.Now.Second == 0):
        print "Hello, World!";
        waitForNearlyAMinute();
    else:
        setupTimer(0.5, pollForMinuteEdge)