Miscellaneous Debris

Avner Kashtan's Frustrations and Exultations

Waiting for a return value from an asynchronous method

The topic of the post might not make sense, initially. If I invoke a method asynchronously, why would I want to wait for a return value? And if I'm waiting, what's the point of launching it asynchronously?

Well, the most common scenario is when I want to run a process that could take time and might freeze on me, but I want to limit the time it can take and throw an exception if the timeout is exceeded. This means running the main logic in a worker thread, which means we have to find a way to get the result back to the calling method.

A simple pattern for this can take advantage of C#'s anonymous delegates to pass the return value directly into a local variable in the calling method. This means we don't have to rely on a class member to pass the info, which would need more work in a multithreaded scenario. The end result is very simple:

public MyObject GetData(TimeSpan timeout)
{
   MyObject returnedData;
   Thread t = new Thread(
                         delegate() 
                         {
                            // Return value directly to local variable.
                            returnedData = BigComponent.DoHeavyProcessing();
                         });
   t.Start();
   bool success = t.Join(timeout); // Wait for the thread until the timeout expires.
   if (success)
      return returnedData;
   else
      throw new TimeoutException();
}

Behind the scenes, the compiler will build a new anonymous type for my delegate method, and will add code to synchronize the new object I create inside the anonymous delegate into the local variable in GetData(). Simple and elegant.

Comments

Guy Burstein said:

Did you try using the ManualResetEvent. I think that this is its whole purpose...

# July 24, 2007 5:11 AM

Avner Kashtan said:

ManualResetEvent won't help me synchronize the data between the threadfunc and the main method. It would just be a replacement for the Thread.Join method, so instead of Join I can flip the ResetEvent in the threadfunc, and wait on it outside. Not much of a difference. It has the same timeout behavior.

# July 24, 2007 5:16 AM

Fernando Felman said:

I see the elegance of the code, but I'm not sure I understand the usage. Your main thread is waiting on the worker to finish and it blocks all other processing from happening. Or am I missing something?

# July 24, 2007 6:27 AM

Avner Kashtan said:

Exactly. I don't want to run this asynchronously. I want it synchronous - by cancellable. If BigComponent doesn't give me any mechanisms to abort DoHeavyProcessing() after a given timeout, this technique does.

# July 24, 2007 6:58 AM

Avner Kashtan said:

Last comment should read "synchronous - BUT cancellable".

# July 24, 2007 7:06 AM

KevinAG said:

If you do timeout, you should consider calling .Abort() on the other thread before raising your own exception.  Otherwise, that thread will just continue to run and you'll loose reference to it when your method exits.

# November 8, 2010 4:04 PM