Лучший подход с таймером в консольном пакетном приложении С#

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

  1. Подключиться к источникам данных
  2. обрабатывать пакет до истечения времени ожидания или завершения обработки. "Сделайте что-нибудь с источниками данных"
  3. изящно остановите консольное приложение.

связанный вопрос: Как добавить таймер в С# консольное приложение


person Johan Bresler    schedule 09.10.2008    source источник
comment
Следует ли использовать таймер для запуска тайм-аута на шаге 2?   -  person Larry    schedule 09.10.2008
comment
Да, я так полагаю. Я немного смущен всеми подходами и тем, как именно их использовать для обработки до истечения времени ожидания.   -  person Johan Bresler    schedule 09.10.2008
comment
Согласно комментарию в ответе ниже, обработка может завершиться до того, как произойдет событие тайм-аута.   -  person Johan Bresler    schedule 09.10.2008


Ответы (3)


Извините за то, что это целое консольное приложение... но вот полноценное консольное приложение, которое поможет вам начать работу. Опять же, я извиняюсь за так много кода, но все остальные, кажется, дают ответ «о, все, что вам нужно сделать, это сделать это» :)

using System;
using System.Collections.Generic;
using System.Threading;

namespace ConsoleApplication1
{
    class Program
    {
        static List<RunningProcess> runningProcesses = new List<RunningProcess>();

        static void Main(string[] args)
        {
            Console.WriteLine("Starting...");

            for (int i = 0; i < 100; i++)
            {
                DoSomethingOrTimeOut(30);
            }

            bool isSomethingRunning = false;

            do
            {
                foreach (RunningProcess proc in runningProcesses)
                {
                    // If this process is running...
                    if (proc.ProcessThread.ThreadState == ThreadState.Running)
                    {
                        isSomethingRunning = true;

                        // see if it needs to timeout...
                        if (DateTime.Now.Subtract(proc.StartTime).TotalSeconds > proc.TimeOutInSeconds)
                        {
                            proc.ProcessThread.Abort();
                        }
                    }
                }
            }
            while (isSomethingRunning);

            Console.WriteLine("Done!");    

            Console.ReadLine();
        }

        static void DoSomethingOrTimeOut(int timeout)
        {
            runningProcesses.Add(new RunningProcess
            {
                StartTime = DateTime.Now,
                TimeOutInSeconds = timeout,
                ProcessThread = new Thread(new ThreadStart(delegate
                  {
                      // do task here...
                  })),
            });

            runningProcesses[runningProcesses.Count - 1].ProcessThread.Start();
        }
    }

    class RunningProcess
    {
        public int TimeOutInSeconds { get; set; }

        public DateTime StartTime { get; set; }

        public Thread ProcessThread { get; set; }
    }
}
person Timothy Khouri    schedule 28.11.2008
comment
Спасибо за это, в моем случае чем больше, тем лучше. :) - person Johan Bresler; 28.11.2008

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

DateTime runUntil = DataTime.Now.Add(timeout);
forech(Task task in tasks)
{
   if(DateTime.Now >= runUntil)
   {
        throw new MyException("Timeout");
   }
   Process(task);
}

В противном случае вам нужно перейти на многопоточность, что всегда сложнее, потому что вам нужно выяснить, как завершить вашу задачу посередине, не вызывая побочных эффектов. Вы можете использовать таймер из System.Timers: http://msdn.microsoft.com/en-us/library/system.timers.timer(VS.71).aspx или Thread.Sleep. Когда происходит событие тайм-аута, вы можете завершить поток, который выполняет фактическую обработку, очистить и завершить процесс.

person Grzenio    schedule 09.10.2008
comment
Спасибо, похоже на предыдущий ответ - попробую и посоветую - person Johan Bresler; 09.10.2008
comment
Будет ли ваша процедура на самом деле истечена по тайм-ауту и ​​не застрянет на: Process(task); ? - person Johan Bresler; 09.10.2008

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

РЕДАКТИРОВАТЬ: Вот некоторый псевдокод, чтобы попытаться уточнить:

List<DataSource> dataSources = ConnectToDataSources();
TimeSpan timeout = GetTimeoutFromConfiguration(); // Or have it passed in!
DateTime endTime = DateTime.UtcNow + timeout;

bool finished = false;
while (DateTime.UtcNow < endTime && !finished)
{
    // This method should do a small amount of work and then return
    // whether or not it's finished everything
    finished = ProcessDataSources(dataSources);
}

// Done - return up the stack and the console app will close.

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

person Jon Skeet    schedule 09.10.2008
comment
Это я немного запутался с подходами - я уверен, что это просто. Период ожидания будет считан из таблицы параметров, которая может быть любой, поскольку вы продолжаете обработку в течение «n периода», а затем останавливаете - person Johan Bresler; 09.10.2008
comment
Я должен добавить, что обработка может быть завершена до того, как произойдет событие тайм-аута. - person Johan Bresler; 09.10.2008
comment
Будет ли ваша подпрограмма фактически истечена по тайм-ауту, т.е. не застрянет ли она на: finish = ProcessDataSources(dataSources); ? - person Johan Bresler; 09.10.2008
comment
Комментарий важен: ProcessDataSources должен выполнять небольшой объем работы. По сути, вы должны иметь совместную проверку, чтобы увидеть, истекло ли время. Вы можете прервать ветку откуда угодно, но это просто отвратительно и опасно. - person Jon Skeet; 09.10.2008
comment
Похоже, у меня не будет выбора, кроме как прервать из другого места, потому что тайм-аут должен как-то остановить обработку - что делать, я думаю сам :) - person Johan Bresler; 09.10.2008
comment
Позвольте мне описать общую картину: это будет запланированное задание в планировщике агента сервера sql, которое запустит консольное приложение. Консольное приложение прочитает параметры, а затем запустит внешний класс MSDynamics AX. Он не имеет никакого контроля над процессом, кроме прерывания начатого задания. - person Johan Bresler; 09.10.2008