Question

Which is the best timer approach for a C# console batch application that has to process as follows:

  1. Connect to datasources
  2. process batch until timeout occurs or processing complete. "Do something with datasources"
  3. stop console app gracefully.

related question: How do you add a timer to a C# console application

Was it helpful?

Solution

Sorry for this being an entire console app... but here's a complete console app that will get you started. Again, I appologize for so much code, but everyone else seems to be giving a "oh, all you have to do is do it" answer :)

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; }
    }
}

OTHER TIPS

It depends on how accurate do you want your stopping time to be. If your tasks in the batch are reasonably quick and you don't need to be very accurate, then I would try to make it single threaded:

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

Otherwise you need to go mulithreaded, which is always more difficult, because you need to figure out how to terminate your task in the middle without causing side effects. You could use the Timer from System.Timers: http://msdn.microsoft.com/en-us/library/system.timers.timer(VS.71).aspx or Thread.Sleep. When the time-out event occurs you can terminate the thread that does the actual processing, clean up and end the process.

When you say "until timeout occurs" do you mean "keep processing for an hour and then stop"? If so, I'd probably just make it very explicit - work out at the start when you want to finish, then in your processing loop, do a check for whether you've reached that time or not. It's incredibly simple, easy to test etc. In terms of testability, you may want a fake clock which would let you programmatically set the time.

EDIT: Here's some pseudocode to try to clarify:

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.

That's just using the built-in clock rather than a clock interface which can be mocked, etc - but it probably makes the general appropriate simpler to understand.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top