Home > ASP.NET, C#, Optimization, Programming, Tips & Tricks > C# Set method timeout using Generics

C# Set method timeout using Generics

I’m pretty sure all of you know the WebRequest and it’s derived class HttpWebRequest.
And what a marvelous property both of them have – the TimeOut.

Yesterday I had to write some app that reads files located on some remote computer.
As I knew already this ins’t such a good practice, because your code can just hang/freeze for seconds waiting for that UNC path to become available or just checking it’s existence.

Still, I had to provide some quick solution and “hoping for the best” wasn’t good enough.
So, this is what I came up with.

        public static T Limex<T>(Func<T> F, int Timeout, out bool Completed)
        {
            T result = default(T);
            Thread thread = new Thread(() => result = F());
            thread.Start();
            Completed = thread.Join(Timeout);
            if (!Completed) thread.Abort();
            return result;
        }

        // Overloaded method, for cases when we don't 
        // need to know if the method was terminated
        public static T Limex<T>(Func<T> F, int Timeout)
        {
            bool Completed;
            return Limex(F, Timeout, out Completed);
        }

The usage is very simple, just pass any method (declared or anonymous) and the desired Timeout in milliseconds to the Limex.

Example :

bool Completed;
string Content = Limex(() => File.ReadAllText(@"\\unc\dir\file.ext")
                       ,100 // milliseconds
                       ,out Completed);

if (Completed) 
   // Do something
else
  // Do something else

Comments and suggestions for improvement are welcome and will be gratefully appreciated

VN:F [1.9.22_1171]
Rating: 4.6/5 (19 votes cast)
C# Set method timeout using Generics, 4.6 out of 5 based on 19 ratings
  1. Joe Chung
    July 16th, 2009 at 11:07 | #1

    Line 15 in the code is wrong. It looks like you renamed LimitedExecute to LimEx but didn’t catch this instance of the method name.

    VA:F [1.9.22_1171]
    Rating: 5.0/5 (1 vote cast)
  2. July 16th, 2009 at 11:36 | #2

    @Joe Chung
    Thanks, fixed it.

    VN:F [1.9.22_1171]
    Rating: 5.0/5 (1 vote cast)
  3. July 16th, 2009 at 13:27 | #3

    useful post ! a lot better than quizzes !

    VA:F [1.9.22_1171]
    Rating: 5.0/5 (1 vote cast)
  4. July 16th, 2009 at 13:47 | #4

    @Amr Ellafy
    Thanks, your point is well taken.

    VN:F [1.9.22_1171]
    Rating: 0.0/5 (0 votes cast)
  5. July 16th, 2009 at 14:55 | #5

    Nice idea, but I think creating a new thread is a bit heavyweight, especially if the wrapped function needs to be called many times a second. Why not use the standard .Net async pattern? Something like this (untested):

    public static T Limex(Func F, int Timeout, out bool Completed)
    {
    var iar = F.BeginInvoke(null, new object());
    if (iar.AsyncWaitHandle.WaitOne(Timeout))
    {
    Completed = true;
    return F.EndInvoke(iar);
    }

    Completed = false;
    return default(T);
    }

    VA:F [1.9.22_1171]
    Rating: 4.5/5 (2 votes cast)
  6. July 16th, 2009 at 15:43 | #6

    @Russ
    Ok, but where do you abort the Func ? If you don’t it it will just continue running.

    VN:F [1.9.22_1171]
    Rating: 0.0/5 (0 votes cast)
  7. July 16th, 2009 at 15:50 | #7

    @Russ
    BTW, as i recall BeginInvoke also creates a new thread, so…

    VN:F [1.9.22_1171]
    Rating: 0.0/5 (0 votes cast)
  8. July 16th, 2009 at 16:53 | #8

    Well, you’ve changed your code – when I wrote my reply there wasn’t a thread abort in there, so my alternative was equivalent.

    Using Thread.Abort is regarded as a bad thing for a number of reasons. For a start, any function that you wrap this way that uses expensive resources or shared state will need to handle ThreadAbortException to make sure it leaves things in a sane state. On 64-bit systems, aborting a thread can cause resource leaks. In .Net 1.1, coding this way can even break a finally block. http://msdn.microsoft.com/en-us/library/system.threading.thread.abort.aspx

    If you REALLY want an equivalent, however, you could rewrap F in an Action delegate that also captures Thread.CurrentThread, then abort that thread after a timeout. I wouldn’t recommend it though. If it’s that important that the wrapped function is killed, execute it in its own appdomain.

    VA:F [1.9.22_1171]
    Rating: 0.0/5 (0 votes cast)
  9. July 16th, 2009 at 16:57 | #9

    Also, BeginInvoke will use a threadpool thread, not a new thread. Therefore you don’t have the initialisation cost of creating a new thread, and you don’t suck up another meg of memory for thread stack space.

    VA:F [1.9.22_1171]
    Rating: 0.0/5 (0 votes cast)
  10. July 16th, 2009 at 17:05 | #10

    @Russ
    You right, i accidentally deleted the .Abort() line while writing this post (my mistake) – without it this code would make no sense ( funny nobody noticed it ).

    About the AppDomain – i thought about it, but isn’t it even heavier ?
    In my case we are talking about thousands very short (50ms) operations.

    VN:F [1.9.22_1171]
    Rating: 0.0/5 (0 votes cast)
  11. July 16th, 2009 at 17:12 | #11

    Yes, in fairness an appdomain is even heavier.

    VA:F [1.9.22_1171]
    Rating: 0.0/5 (0 votes cast)
  12. July 16th, 2009 at 17:25 | #12

    @Russ
    About the ThreadPool – it’s exactly what i don’t want. It’s supposed to be executed as a synchronous operation which won’t be possible using ThreadPool.

    VN:F [1.9.22_1171]
    Rating: 0.0/5 (0 votes cast)
  13. July 16th, 2009 at 18:46 | #13

    That’s why you use iar.AsyncWaitHandle.WaitOne(Timeout) – it blocks on the calling thread while the operation takes place on the threadpool thread. That’s equivalent to your Join call. It’s exactly analogous to your approach, it just doesn’t need to create a new thread.

    VA:F [1.9.22_1171]
    Rating: 0.0/5 (0 votes cast)
  14. July 16th, 2009 at 19:34 | #14

    @Russ
    Ok, i agree that this is a much safer way, but correct me if i am wrong – your solution won’t allow to terminate the thread after the timeout and it will continue running without releasing the queue.

    Let’s say you are executing some loop with 100ms timeout – you will get the result after 100ms (null or not), but the loop will continue executing till it stops (which is very bad and dangerous)

    VN:F [1.9.22_1171]
    Rating: 0.0/5 (0 votes cast)
  15. July 16th, 2009 at 20:01 | #15

    @Russ
    Just to visualize my previous reply :

            // This is your solution
            public static T SafeLimex<T>(Func<T> F, int Timeout, out bool Completed)
            {
                var iar = F.BeginInvoke(null, new object());
                if (iar.AsyncWaitHandle.WaitOne(Timeout))
                {
                    Completed = true;
                    return F.EndInvoke(iar);
                }
                
                Completed = false;
                return default(T);
            }
    

    Now let’s try to execute the next code :

                for (int n = 0; n < 100; n++)
                {
                    var r = SafeLimex(() => { 
                        Console.WriteLine("\n\rThread : " + Thread.CurrentThread.ManagedThreadId); 
                        Thread.Sleep(100); 
                        for (int i = 0; i < 1000; i++) { Console.Write(i); } 
                        return "result"; 
                    }, 120, out Finished);
                    Console.WriteLine("\n\r" + "Result : " + r + ", Finished : " + Finished);
                }
    

    You will get something like this :

    Thread : 7
    0123456789101….454647484950…561
    Result : , Finished : False
    5625635645……………………………….601
    Thread : 11
    602603604…………………………………698
    Result : , Finished : False
    699……………………………………………999

    VN:F [1.9.22_1171]
    Rating: 5.0/5 (3 votes cast)
  16. July 16th, 2009 at 20:21 | #16

    As I said in my second comment, you can terminate if you capture the current thread, e.g.

    Thread t = null;
    Action action = () =>
    {
    t = Thread.CurrentThread;
    F();
    };

    Then you call BeginInvoke on action instead of F. The action delegate will execute on the threadpool, so in that context Thread.CurrentThread will be your threadpool thread. If WaitOne returns false, you call t.Abort(). This is nasty for reasons I mentioned earlier, with the added problem that it gradually wrecks the threadpool.

    I’m not necessarily arguing that my way is better, just an alternative. Fundamentally, there is no first-class language support for what you’re trying to do, so any solution is going to be hacky in some respect. If your solution is working for you, don’t sweat it – just be aware of the risks, particularly with Thread.Abort().

    VA:F [1.9.22_1171]
    Rating: 5.0/5 (3 votes cast)
  17. July 16th, 2009 at 23:23 | #17

    @Russ , I’ll be sure to check your solution in a runtime environment.

    Thanks for a great discussion and i hope to see you in my future posts.

    VN:F [1.9.22_1171]
    Rating: 5.0/5 (2 votes cast)
  18. nohorse
    February 19th, 2010 at 03:08 | #18

    Don’t forget to eventually cleanup the async call:

    private static T InvokeWithTimeout(Func operation)
    {
    T result = default(T);
    if (timeout == 0)
    {
    result = operation.Invoke();
    }
    else
    {
    var async = operation.BeginInvoke(AsyncCleanup,operation);
    if (async.AsyncWaitHandle.WaitOne(timeout))
    {
    result = operation.EndInvoke(async);
    }
    }
    return result;
    }

    private static void AsyncCleanup(IAsyncResult result)
    {
    var operation = result.AsyncState as Func;
    if (operation != null)
    {
    operation.EndInvoke(result);
    }
    }

    VA:F [1.9.22_1171]
    Rating: 0.0/5 (0 votes cast)
  19. August 16th, 2010 at 15:09 | #19

    i’m new… promise to post around more time after time!

    VA:F [1.9.22_1171]
    Rating: 0.0/5 (0 votes cast)
  20. David
    November 23rd, 2010 at 21:50 | #20

    What about for calling void methods?

    VA:F [1.9.22_1171]
    Rating: 0.0/5 (0 votes cast)
    • November 24th, 2010 at 18:30 | #21

      Hi, David
      You can overload it with something like this :

      public static void Limex(Action A, int Timeout, out bool Completed)
      {
      Limex(new Func(() => { A(); return null; }), Timeout, out Completed);
      }

      and call it as following :

      static void Test()
      {
      Thread.Sleep(5000);
      }

      Limex(Test, 100, out Completed);

      VN:F [1.9.22_1171]
      Rating: 0.0/5 (0 votes cast)
  21. September 18th, 2011 at 07:59 | #22

    Hi!
    Very interesting name by the forum kossovsky.net

    This situation is familiar to me. Is ready to help.

    The possession of unlimited power will make a despot of almost any man.

    Buy-buy!

    VA:F [1.9.22_1171]
    Rating: 0.0/5 (0 votes cast)
  22. Paresh
    January 21st, 2012 at 14:18 | #23

    Hi,

    Thanks. This article is very useful.

    I am getting compile time error for void return methods:

    public static void Limex(Action A, int Timeout, out bool Completed)
    {
    Limex(new Func(() => { A(); return null; }), Timeout, out Completed);
    }

    I have added above function. Here Func is generating error. Below is the compile time error message:

    Using the generic type ‘System.Func’ requires 10 type arguments

    Am I doing anything wrong here?

    Can you please look into this?

    VA:F [1.9.22_1171]
    Rating: 0.0/5 (0 votes cast)
  23. January 22nd, 2013 at 13:02 | #24

    Thank you so much Xander. I was looking for the same kind of functionality and you saved my day. Let me try to implement it and see how it works. Thanks again.

    VA:F [1.9.22_1171]
    Rating: 0.0/5 (0 votes cast)
  24. Kiquenet
    April 12th, 2013 at 12:32 | #25

    It’s great, thanks.

    which is the best way to do it without deadlocks ?

    your source code fully safe without deadlocks?

    http://stackoverflow.com/questions/299198/implement-c-sharp-generic-timeout

    a deadlock waiting to happen (I’m surprised you haven’t observed it yet). Your call to watchedThread.Abort() is inside a lock, which also needs to be acquired in the finally block. This means while the finally block is waiting for the lock (because the watchedThread has it between Wait() returning and Thread.Abort()), the watchedThread.Abort() call will also block indefinitely waiting for the finally to finish (which it never will). Therad.Abort() can block if a protected region of code is running – causing deadlocks, see – msdn.microsoft.com/en-us/library/ty8d3wta.aspx

    VA:F [1.9.22_1171]
    Rating: 4.0/5 (1 vote cast)
  1. No trackbacks yet.

Subscribe without commenting

SEO Powered by Platinum SEO from Techblissonline
watch free movies