views:

365

answers:

3

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.

+1  A: 

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

Valentin Vasiliev
Thanks, that's kind of blowing my mind at the moment but I will continue to study it.
James
@Valentin Vasiliev: Unfortunately, it's not the pattern he is looking for. He is looking for the event-based async pattern.
casperOne
Event-based async patterns are generally simpler, so I hope he'll handle the easier one :-)
Valentin Vasiliev
+4  A: 

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

casperOne
Yes, that looks very promising...
James
I've been reading through the documentation and that's exactly what I need. Thank you.
James
A: 

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.

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