Sunday, April 3, 2011

How do I create a class with asynchronous capabilities (similiar to SqlCommand or WebRequest)?

I've been reading a lot about asynchronous programming recently, as I need to create a multi-threaded application.

Unfortunately I can't seem to bring my newly acquired knowledge together into one cohesive and useful unit!

I'm hoping someone can give me some pointers on how to construct the following:

  • I have a class that does a lot of different (and time-consuming) tasks in a specified sequence.

  • I'd like to instantiate this class in my Winforms UI thread. eg:

    TaskRunner tr = new TaskRunner();
    
  • I'd like to be able to call a BeginAsync() method (as you can do with lots of the .NET built-in objects). eg:

    tr.BeginAsync();
    
  • I'd like my class to callback to my UI thread when certain events arise (for logging, completion etc).

  • I'd like to be able to cancel the execution of my class. eg:

    tr.CancelAsync();
    

How do I go about building the internals of that class? I can't seem to find anything that talks about how the internals of SqlCommand or WebRequest might work.

From stackoverflow
  • Hope, this example will help you.

    public class MessagingServices
    {
      public static IAsyncResult BeginReverseEcho (TcpClient client,
                                                   AsyncCallback callback,
                                                   object userState)
      {
        var re = new ReverseEcho(  );
        re.Begin (client, callback, userState);
        return re;
      }
    
      public static byte[] EndReverseEcho (IAsyncResult r)
      {
        return ((ReverseEcho)r).End(  );
      }
    }
    
    class ReverseEcho : IAsyncResult
    {
      volatile TcpClient     _client;
      volatile NetworkStream _stream;
      volatile object        _userState;
      volatile AsyncCallback _callback;
      ManualResetEvent       _waitHandle = new ManualResetEvent (false);
      volatile int           _bytesRead = 0;
      byte[]                 _data = new byte [5000];
      volatile Exception     _exception;
    
      internal ReverseEcho(  ) { }
    
      // IAsyncResult members:
    
      public object AsyncState           { get { return _userState;  } }
      public WaitHandle AsyncWaitHandle  { get { return _waitHandle; } }
      public bool CompletedSynchronously { get { return false;       } }
      public bool IsCompleted
      {
       get { return _waitHandle.WaitOne (0, false); }
      }
    
      internal void Begin (TcpClient c, AsyncCallback callback, object state)
      {
        _client = c;
        _callback = callback;
        _userState = state;
        try
        {
          _stream = _client.GetStream(  );
          Read(  );
        }
        catch (Exception ex) { ProcessException (ex); }
      }
    
      internal byte[] End(  )     // Wait for completion + rethrow any error.
      {
        AsyncWaitHandle.WaitOne(  );
        AsyncWaitHandle.Close(  );
        if (_exception != null) throw _exception;
        return _data;
      }
    
      void Read(  )   // This is always called from an exception-handled method
      {
        _stream.BeginRead (_data, _bytesRead, _data.Length - _bytesRead,
                           ReadCallback, null);
      }
    
      void ReadCallback (IAsyncResult r)
      {
        try
        {
          int chunkSize = _stream.EndRead (r);
          _bytesRead += chunkSize;
          if (chunkSize > 0 && _bytesRead < _data.Length)
          {
            Read(  );       // More data to read!
            return;
          }
          Array.Reverse (_data);
          _stream.BeginWrite (_data, 0, _data.Length, WriteCallback, null);
        }
        catch (Exception ex) { ProcessException (ex); }
      }
    
      void WriteCallback (IAsyncResult r)
      {
        try { _stream.EndWrite (r); }
        catch (Exception ex) { ProcessException (ex); return; }
        Cleanup(  );
      }
    
      void ProcessException (Exception ex)
      {
        _exception = ex;   // This exception will get rethrown when
        Cleanup();         // the consumer calls the End(  ) method.
      }
    
      void Cleanup(  )
      {
        try
        {
          if (_stream != null) _stream.Close(  );
        }
        catch (Exception ex)
        {
          if (_exception != null) _exception = ex;
        }
        // Signal that we're done and fire the callback.
        _waitHandle.Set(  );
        if (_callback != null) _callback (this);
      }
    }
    

    Example is taken from C# 3.0 in a Nutshell, 3rd Edition by Joseph Albahari; Ben Albahari

    James : Thanks, that's kind of blowing my mind at the moment but I will continue to study it.
    casperOne : @Valentin Vasiliev: Unfortunately, it's not the pattern he is looking for. He is looking for the event-based async pattern.
    Valentin Vasiliev : Event-based async patterns are generally simpler, so I hope he'll handle the easier one :-)
  • For this operation, you want to use the event-based asynchronous pattern (as opposed to the IAsyncResult design pattern). For more information, see the section of the MSDN documentation titled "Event-based Asynchronous Pattern Overview", located at:

    http://msdn.microsoft.com/en-us/library/wewwczdw.aspx

    James : Yes, that looks very promising...
    James : I've been reading through the documentation and that's exactly what I need. Thank you.
  • You should also consider the BackgroundWorker object that has a lot of that functionality built in for doing time intensive or behind the scene processes.

    This article has a nice tutorial outlining the entire process, including having a progress bar displayed.

    James : The BackgroundWorker is useful, and I built half a prototype with it, but it's not quite what I need.

0 comments:

Post a Comment