Question

I have a method which sometimes takes more than a minute to execute. I want to create a task to monitor the time it takes to execute this method. If the method executes with-in 2 minute, I should return output of the first task else I should throw an exception. I am using .net framework 4.0 with C# as the language.

I cannot use Microsoft Reactive Extensions in this case, as it frees the main thread. I do not want to release the main thread until following happens

  1. Timeout
  2. Data is returned
  3. Any other exception occurs

Please provide your suggestions.

Was it helpful?

Solution

Ideally, you should use CancellationTokenSource with timeout and observe its CancellationToken inside your method. If that's not possible, you can use Task.WhenAny. The following implementation of MethodAsync should suit your scenario for .NET 4.0:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp
{
    class Program
    {
        // the method to monitor and timeout
        int Method()
        {
            Thread.Sleep(3000); // sleep for 3s
            return 42;
        }

        Task<int> MethodAsync(int timeout)
        {
            // start the Method task
            var task1 = Task.Run(() => Method());

            // start the timeout task
            var task2 = Task.Delay(timeout);

            // either task1 or task2
            return Task.WhenAny(task1, task2).ContinueWith((task) =>
            {
                if (task.Result == task1)
                    return task1.Result;
                throw new TaskCanceledException();
            });
        }

        // The entry of the console app
        static void Main(string[] args)
        {
            try
            {
                // timeout in 6s
                int result = new Program().MethodAsync(6000).Result;
                Console.WriteLine("Result: " + result);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error: " + ex.Message);
            }
            Console.WriteLine("Hit enter to exit.");
            Console.ReadLine();
        }
    }
}

Below is the version of MethodAsync using async/await, if you can use Microsoft.Bcl.Async:

async Task<int> MethodAsync(int timeout)
{
    // start the Method task
    var task1 = Task.Run(() => Method());

    // start the timeout task
    var task2 = Task.Delay(timeout);

    // either task1 or task2
    var task = await TaskEx.WhenAny(task1, task2);
    if (task == task1)
        return task1.Result;

    throw new TaskCanceledException();
}

OTHER TIPS

I would look at using Microsoft's Reactive Framework. It would be super easy.

Here it is:

var query =
    Observable
        .Start(() => longRunningTask())
        .Timeout(TimeSpan.FromMinutes(2.0));

You then just subscribe to the observable to get the result out.

query.Subscribe(
    n => Console.WriteLine(n),
    ex => Console.WriteLine(ex.Message));

That's it. You either get the result out - the n - or you get the exception - the ex. Simple as that. And it's all run in a background task for you.


Use this kind of code to have the long-running computation complete on timeout.

var query =
    Observable
        .Create<int>(o =>
            Scheduler.Default.AsLongRunning().ScheduleLongRunning(c =>
            {
                for (var i = 0; i < 100; i++)
                {
                    Thread.Sleep(100);
                    if (c.IsDisposed)
                    {
                        break;
                    }
                }
                if (!c.IsDisposed)
                {
                    o.OnNext(42);
                }
                o.OnCompleted();
            }))
        .Timeout(TimeSpan.FromMinutes(2.0));

The key thing to understand with Rx is that cancellation is all based on the IDisposable interface. In fact the Subscribe method returns an IDisposable that allows the subscribing code to early terminate an Rx subscription. But when an observable completes (either with an OnComplete or OnError then the underlying disposable is disposed. When there are a chain of operators then every disposable in the chain are disposed.

So this code returns merely returns a disposable that once disposed will terminate the computation. Quite simple really.

You could use a TimeOut class that you use to throw a TimeoutException after a given time. Something like:

const int twoMinutes = 120000;
TimeOutHelper.ExecuteMethod(() =>
{
    byte[] buffer = new byte[2048];
    int c = -1;

    do
    {
        c = Service.Read(buffer, 0, 2048);
    } while (c == 0);

}, twoMinutes);

Code:

public  static class TimeOutHelper
{
    private class ObjectState
    {
        public Action predict { get; set; }
        public AutoResetEvent autoEvent { get; set; }
    }

    public static bool ExecuteMethod(Action predict, int timeOutInterval, int retryCounter = 1)
    {
        bool IsFinished = false;
        ObjectState objectState = new ObjectState();
        AutoResetEvent autoEvent = new AutoResetEvent(false);
        int timeOutInt = timeOutInterval > 0 ? timeOutInterval: Timeout.Infinite;
        objectState.autoEvent = autoEvent;
        objectState.predict = predict;
        Timer timer = null;

        var callbackMethod = new TimerCallback(TimerCallbackMethod);
        timer = new Timer(callbackMethod, objectState, 0, timeOutInt+5);

        try
        {
            for (int i = 0; i < retryCounter; i++)
            {
                var isSignal = autoEvent.WaitOne(timeOutInt, false);
                if (isSignal)
                 break; 
                if (retryCounter - 1 == i) throw new TimeoutException();
                else
                    timer.Change(0, timeOutInt);
            }
        }
        finally
        {
            IsFinished = true;

            if (autoEvent != null)
                autoEvent.Dispose();
            if (timer != null)
                timer.Dispose();
        }
        return IsFinished;
    }

    private static void TimerCallbackMethod(object state)
    {
        var objectSate = (ObjectState)state;
        var predict = (Action)objectSate.predict;
        predict();
        if(objectSate!=null && !objectSate.autoEvent.SafeWaitHandle.IsClosed)
            objectSate.autoEvent.Set();
    }
}

you may use timer as thread.

System.Threading.Timer timer;
timer = new System.Threading.Timer(new TimerCallback(Module_Watchdog), null, 0, 1000);

Module_Watchdog() executes at specified intervals(1000 ms here). Note that Module_Watchdog() does not execute on the thread that created the timer; it executes on a ThreadPool thread supplied by the system.

private void Module_Watchdog(object obj)
{
   // Block here output of task1 as timeout already occured..
   try
   {
   throw new TimeoutException();
   }
   finally
   {

   }
}

and final implementation will be like this:

...
...
timer = new System.Threading.Timer(new TimerCallback(Module_Watchdog), null, 0, 1000);
Moduleunder_Watchdog();
timer.Change(Timeout.Infinite, Timeout.Infinite); //disable

you can also in runtime modify timings, if your code have find that it will be taking more time to process

 timer.Change(0, 1000); //enable

if you need to read the value of time taken you can use stopwatch. More information on the stopwatch could be at http://msdn.microsoft.com/en-us/library/system.diagnostics.stopwatch.aspx

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