Search code examples
c#multithreadingc#-3.0asynchronoustimeout

Implement C# Generic Timeout


I am looking for good ideas for implementing a generic way to have a single line (or anonymous delegate) of code execute with a timeout.

TemperamentalClass tc = new TemperamentalClass();
tc.DoSomething();  // normally runs in 30 sec.  Want to error at 1 min

I'm looking for a solution that can elegantly be implemented in many places where my code interacts with temperamental code (that I can't change).

In addition, I would like to have the offending "timed out" code stopped from executing further if possible.


Solution

  • The really tricky part here was killing the long running task through passing the executor thread from the Action back to a place where it could be aborted. I accomplished this with the use of a wrapped delegate that passes out the thread to kill into a local variable in the method that created the lambda.

    I submit this example, for your enjoyment. The method you are really interested in is CallWithTimeout. This will cancel the long running thread by aborting it, and swallowing the ThreadAbortException:

    Usage:

    class Program
    {
    
        static void Main(string[] args)
        {
            //try the five second method with a 6 second timeout
            CallWithTimeout(FiveSecondMethod, 6000);
    
            //try the five second method with a 4 second timeout
            //this will throw a timeout exception
            CallWithTimeout(FiveSecondMethod, 4000);
        }
    
        static void FiveSecondMethod()
        {
            Thread.Sleep(5000);
        }
    

    The static method doing the work:

        static void CallWithTimeout(Action action, int timeoutMilliseconds)
        {
            Thread threadToKill = null;
            Action wrappedAction = () =>
            {
                threadToKill = Thread.CurrentThread;
                try
                {
                    action();
                }
                catch(ThreadAbortException ex){
                   Thread.ResetAbort();// cancel hard aborting, lets to finish it nicely.
                }
            };
    
            IAsyncResult result = wrappedAction.BeginInvoke(null, null);
            if (result.AsyncWaitHandle.WaitOne(timeoutMilliseconds))
            {
                wrappedAction.EndInvoke(result);
            }
            else
            {
                threadToKill.Abort();
                throw new TimeoutException();
            }
        }
    
    }