views:

198

answers:

2

I am learning threading.My intension is to pass some values to a method for calculation,if the result will not be returned within 20 ms,i will report "Operation timeout".Based on my understading i have implemented the code as follows:

public delegate long CalcHandler(int a, int b, int c);


public static void ReportTimeout()
    {
        CalcHandler doCalculation = Calculate;

        IAsyncResult asyncResult = 
        doCalculation.BeginInvoke(10,20,30, null, null);

        if (!asyncResult.AsyncWaitHandle.WaitOne(20, false))
        {
            Console.WriteLine("..Operation Timeout...");
        }

        else
       {
        // Obtain the completion data for the asynchronous method.
        long val;
        try
        {
            val=  doCalculation.EndInvoke(asyncResult);
            Console.WriteLine("Calculated Value={0}", val);
        }
        catch
        {
            // Catch the exception
        }
     }

    } 

public static long Calculate(int a,int b,int c)
     {
        int m;
        //for testing timeout,sleep is introduced here.
        Thread.Sleep(200);
        m = a * b * c;
        return m;
    }

Questions :

(1) Is it the proper way to report timeout ?

(2) I will not call EndInvoke() ,if the time is out.In such case calling the EndInvoke() is mandatory?

(3) I heard that

"Even if you do not want to handle the return value of your asynchronous method, you should call EndInvoke; otherwise, you risk leaking memory each time you initiate an asynchronous call using BeginInvoke"

What is the risk associated with memory ? can you give example ?

+1  A: 

From a timeout value, yes, this is going to set the timeout as you would expect.

As for the memory risk. There is a risk that if you do not call EndInvoke that there will still be a reference to the delegate, and any resources used might not be garbage collected until the application exits. The exact implementation is not 100% documented from what I found, but could be confirmed with a tool like ANTS Profiler.

Here is a helpful discussion.

Mitchel Sellers
+1  A: 

Answers

(1) Usually you just throw System.TimeoutException

(2) The reason EndInvoke() is required is that if return values (or exceptions thrown) have memory allocations the GC may not clean it up as quickly. I have not seen a case where this was a big problem... but that's just me.

If your really worried about it you can just call ThreadPool.QueueUserWorkItem which is mostly what the above async invoke will do. (note: can be a problem in WinForms or other thread-dependent contexts). Anyway you can run your own work item as follows:

 public static long Calc(CalcHandler fn, int a, int b, int c)
 {
  return Run<long>(TimeSpan.FromSeconds(20), delegate { return fn(a, b, c); });
 }

 public static T Run<T>(TimeSpan timeout, Func<T> operation)
 {
  Exception error = null;
  T result = default(T);

  ManualResetEvent mre = new ManualResetEvent(false);
  System.Threading.ThreadPool.QueueUserWorkItem(
   delegate(object ignore)
   {
    try { result = operation(); }
    catch (Exception e) { error = e; }
    finally { mre.Set(); }
   }
  );
  if (!mre.WaitOne(timeout, true))
   throw new TimeoutException();
  if (error != null)
   throw new TargetInvocationException(error);
  return result;
 }
csharptest.net