Еще один способ реализовать таймер в C # - PullRequest
0 голосов
/ 08 января 2012

Я должен запускать функцию каждые n минут в службе Windows. Когда закончите, начните снова. Если функция запущена, она не может начаться снова, пока не закончится Изначально у меня была такая идея:

void function()
{
  while(true)
  {
    if(!is_running)
    {
      function_to_be_repeated();
      is_running = false;
    }
    else
    {
      Thread.Sleep(some_time); // wait to start again
    }
  }
}

Тогда я нашел это http://www.albahari.com/threading/part3.aspx:

 bool running = false;

 static void Main()
  {
    // First interval = 5000ms; subsequent intervals = 1000ms
    Timer tmr = new Timer (function_to_be_repeated, "...", 5000, 1000);
    Console.ReadLine();    
    tmr.Dispose();         // This both stops the timer and cleans up.
  }

  static void function_to_be_repeated(object data)
  {
      if(!running)
      {
        running = true;
        // do some stuff...

        running = false;
      }

  }

Но это останавливается, когда я нажимаю Enter

Есть ли другой способ сделать это лучше?

Ответы [ 3 ]

1 голос
/ 08 января 2012

Я не совсем уверен, в чем вопрос, когда вы упоминаете службу Windows и клавишу Enter, оканчивающую ваше приложение, тогда вы упоминаете лучшую реализацию.

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

do
{

}
while (!Console.ReadLine().Equals("exit",
                                   StringComparison.InvariantCultureIgnoreCase)
       );

вместо

Console.ReadLine(); 

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

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

class Poll : IDisposable
{
    private TimeSpan polledSpan;
    WaitHandle[] handles = new WaitHandle[2];
    ManualResetEvent exit = new ManualResetEvent(false);
    Thread thread;
    public Poll(int polledTime)
    {
        polledSpan = new TimeSpan(0, 0, polledTime);
        thread = new Thread(new ThreadStart(Start));
        thread.Start();
    }

    private void Start()
    {            
        AutoResetEvent reset = new AutoResetEvent(false);
        handles[0] = reset;
        handles[1] = exit;
        bool run = true;
        while (run)
        {                
            int result = WaitHandle.WaitAny(handles, 
                                           (int)polledSpan.TotalMilliseconds, 
                                           false);

            switch(result)
            {
                case WaitHandle.WaitTimeout:
                    run = StuffToDo();
                    break;
                case 1:
                case 0:
                    run = false;
                    break;
            }                
        }            
    }

    private bool StuffToDo()
    {
        try
        {
            Console.WriteLine("Test");
            return true;
        }
        catch
        {
            return false;
        }
    }

    public void Dispose()
    {
        exit.Set();
        if (thread != null)
        {
            thread.Join(10000);
        }
        exit = null;
        handles = null;
    }
}

и в основном методе

Poll p = new Poll(1);
do
{

}
while (!Console.ReadLine().Equals("exit",
                                  StringComparison.InvariantCultureIgnoreCase));
p.Dispose();
0 голосов
/ 08 января 2012

Я не уверен, с чем у вас проблемы, но вот пример использования таймера, который фактически защитит себя от работы параллельно с самим собой (что System.Threading.Timer не делает по умолчанию) ,

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

class Program
{
    static void Main(string[] args)
    {
        var timeBetweenTicks = (long) TimeSpan.FromSeconds(1).TotalMilliseconds;
        System.Threading.Timer timer = null;
        Action onTick = () =>
            {
                timer.Change(Timeout.Infinite, Timeout.Infinite);
                try
                {
                    //Work on tick goes here
                    Console.WriteLine(DateTime.Now);
                }
                finally
                {
                    timer.Change(timeBetweenTicks, timeBetweenTicks);
                }
            };
        using (timer = new System.Threading.Timer(_ => onTick(), null, 0, timeBetweenTicks))
        {
            Console.ReadKey();
        }
    }
}

Обратите внимание, что если вы хотите, чтобы время до следующего тика зависело от того, сколько времени занял текущий тик, вам нужно будет добавить немного времени и изменить код в "finally".

0 голосов
/ 08 января 2012

Приведенный выше код, вероятно, выполняется в консольном приложении, поэтому он существует после нажатия ENTER (точнее, из-за Console.Readline()).

Однако вытакже упомянул, что это необходимо в службе Windows.

В службе Windows вы обычно переопределяете методы OnStart() и OnStop() для ServiceBase.В вашем случае сервисный код будет выглядеть так:

private Timer tmr;

protected override void OnStart(string[] args)
{
    tmr = new Timer (function_to_be_repeated, "...", 5000, 1000);
}

protected override void OnStop()
{
    tmr.Dispose();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...