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.3_1094]
Rating: 4.7/5 (15 votes cast)
C# Set method timeout using Generics, 4.7 out of 5 based on 15 ratings

  • DZone
  • Digg
  • Twitter
  • Yahoo Buzz
  • StumbleUpon
  • Delicious
  • Technorati Favorites
  • LiveJournal
  • Reddit
  • Share/Bookmark

A few posts you might find interesting:

  1. C# Inheritance Question
  2. C# Protected Internal
  3. C# LINQ Teaser
  4. SpeedTrace – .NET Profiler and Tracer
  5. C# Image Processing with AForge.NET Framework

  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.3_1094]
    Rating: 5.0/5 (1 vote cast)
  2. July 16th, 2009 at 11:36 | #2

    @Joe Chung
    Thanks, fixed it.

    VN:F [1.9.3_1094]
    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.3_1094]
    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.3_1094]
    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.3_1094]
    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.3_1094]
    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.3_1094]
    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.3_1094]
    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.3_1094]
    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.3_1094]
    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.3_1094]
    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.3_1094]
    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.3_1094]
    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.3_1094]
    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.3_1094]
    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.3_1094]
    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.3_1094]
    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.3_1094]
    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.3_1094]
    Rating: 0.0/5 (0 votes cast)
  1. No trackbacks yet.

Subscribe without commenting

SEO Powered by Platinum SEO from Techblissonline
watch free movies