{STATIC} hippo

...on becoming a great developer...
posts - 38, comments - 12, trackbacks - 0

My Links

Archives

Post Categories

Run generic tasks async (fluent-ly)

 

I saw a post entitled Run Async tasks with Fluent Interface written in response to another post entitled Async without the pain.  I suppose this post is my response to those posts. 

While I liked what I saw there, there were some things I wanted to add (like the ability to block until tasks complete), so I wrote my own class along the same lines.  There are still some things I’d like to add (like the ability to queue tasks into jobs that run in order), but this works for me right now for what I’m doing.

   1: public class AsyncQueueManager
   2:     {
   3:         private ILog _logger = log4net.LogManager.GetLogger(typeof(AsyncQueueManager));
   4:         private ManualResetEvent _WaitHandle = new ManualResetEvent(true);
   5:         private int _QueueCount = 0;
   6:         object locker = new object();
   7:  
   8:         private Action<object> GetActionWrapper(Action<object> action)
   9:         {
  10:             Action<object> actionWrapper = (object obj) =>
  11:                         {
  12:                             try
  13:                             {
  14:                                 action(obj);
  15:                             }
  16:                             catch(Exception ex)
  17:                             {
  18:                                 LogException(action, ex);
  19:                             }
  20:                             finally
  21:                             {
  22:                                 lock (locker)
  23:                                     _QueueCount = _QueueCount - 1;
  24:                                 SetWaitHandle();
  25:                             }
  26:                         };
  27:             return actionWrapper;
  28:         }
  29:         public AsyncQueueManager Queue(Action<object> action)
  30:         {
  31:             lock(locker)
  32:                 _QueueCount = _QueueCount + 1;
  33:             _WaitHandle.Reset();            
  34:             Action<object> actionWrapper = GetActionWrapper(action);
  35:             WaitCallback waitCallback = new WaitCallback(actionWrapper);
  36:             ThreadPool.QueueUserWorkItem(waitCallback);
  37:             return this;
  38:         }
  39:         private void SetWaitHandle()
  40:         {
  41:             if (_QueueCount == 0)
  42:             {
  43:                 _WaitHandle.Set();
  44:             }
  45:         }
  46:  
  47:         public void WaitOne()
  48:         {
  49:             _WaitHandle.WaitOne();
  50:         }
  51:  
  52:         public void WaitOne(TimeSpan timeout)
  53:         {
  54:             _WaitHandle.WaitOne(timeout);
  55:         }
  56:  
  57:         private void LogException(Action<object> action, Exception ex)
  58:         {
  59:             string exceptionString = "Exception occured while running async operation." + 
  60:                 "  Details below." + Environment.NewLine +
  61:                 "Method name:" + action.Method.Name + Environment.NewLine +
  62:                 "Exception:" + ex.ToString();
  63:             _logger.Error(exceptionString);
  64:         }
  65:     }

Running code is as simple as:

   1: int resultA;
   2: int resultB;
   3: int resultC;
   4: var async = new AsyncQueueManager();
   5: async.Queue((object obj) => {
   6:     int result = 0;
   7:     for (int i = 0; i < 1000000; i++)
   8:         result++;
   9:     resultA = result;
  10: });
  11: async.Queue((object obj) => {
  12:     int result = 1;
  13:     for (int i = 0; i < 1000000; i++)
  14:         result += 2;
  15:     resultB = result;
  16: });
  17: async.Queue((object obj) => {
  18:     int result = 1;
  19:     for (int i = 0; i < 1000000; i++)
  20:         result += 3;
  21:     resultC = result;
  22: });
  23: async.Wait(); // wait for these to complete in no particular order
  24:  
  25: Console.WriteLine("resultA: " + resultA);
  26: Console.WriteLine("resultB: " + resultB);
  27: Console.WriteLine("resultC: " + resultC);

Now you’d have to have a pretty slow machine for this to actually take any noticeable time, but you get the idea.

Notice of course how I can set local variables within my in-line Actions.  This is because, as I understand it, the compiler actually creates a separate class to handle these since it knows where you’re going with this.  Of course you could also call methods or do whatever you want here.

Print | posted on Monday, November 09, 2009 11:40 AM | Filed Under [ c# ]

Feedback

Gravatar

# re: Run generic tasks async (fluent-ly)

Very nice!
Is there any way to cancel a running operation using this?
3/23/2010 8:37 PM | Alex
Gravatar

# re: Run generic tasks async (fluent-ly)

how is this Fluent ?
4/26/2010 11:28 AM | Erhan Hosca

Post Comment

Title  
Name  
Email
Url
Comment   
Please add 5 and 3 and type the answer here:

Powered by:
Powered By Subtext Powered By ASP.NET