Asynchronous Design Pattern Using ThreadPool

  • Thread starter Thread starter colinjack
  • Start date Start date
C

colinjack

Hi All,

I've been using the original (non-event based) asynchronous design
pattern using delegates. It works fine but performance wise I know
using the ThreadPool is supposed to be far better, so I wondered if
anyone had any examples of the best way to do this.

I've obviously had a look myself but haven't gotten very far and
although I've implemented a version myself I'm not sure its the best
that can be done.

Thanks,

Colin
 
The way I usually approach this is to create a cached WaitCallBack in
the class, rather than having to create a new one each time I call
ThreadPool.QueueUserWorkItem.

Here's an example of a generic class that I've used before.

It's all based around a data-entry grid (the type is
PortfolioGridControl). It was used in an application that needed to
fire (and forget) events in other grids when the user entered a value
or moved the selected record.

GridCallBackEvent.cs.
The constructor takes a PortfolioGridControl object (used to correctly
fire via Invoke/InvokeRequired) and the delegate to fire (a GridEvent).

It then creates the WaitCallBack passing the address of an internal
function to call (InternalCalledBackEvent) which in turn calls
InvokeGridEvent to correctly invoke the define GridEvent delegate.

A public function, InvokeEvent, is used to fire the required event
passing EventArgs (GridControlEventArgs).

You'll need to remove the reference to PortfolioGridControl if you
don't plan to communicate with controls and just fire gridEvent
directly:

using System;
using System.Windows.Forms;
using System.Threading;
using System.Runtime.CompilerServices;

namespace JHalesCallBacks
{

public class GridCallBackEvent
{

public delegate void GridEvent(GridControlEventArgs e);

private readonly GridEvent gridEvent;
private readonly PortfolioGridControl portfolioGridControl;
private readonly WaitCallback calledbackFromThreadPool;

public
GridCallBackEvent(PortfolioGridControl portfolioGridControl, GridEvent
gridEvent)
{
this.portfolioGridControl = portfolioGridControl;
this.gridEvent = gridEvent;

// create the pointer to our own method (CalledBackEvent) that will
be called back via threadpool
this.calledbackFromThreadPool = new
WaitCallback(InternalCalledBackEvent);
}

public void InvokeEvent(GridControlEventArgs e)
{
// Simple ask the
ThreadPool to fire our WaitCallBack
ThreadPool.QueueUserWorkItem(calledbackFromThreadPool, e);
}


private void InternalCalledBackEvent(object state)
{
InvokeGridEvent(state as GridControlEventArgs);
}


/// This whole method is locked (using MethodImpl) as we don't want
race conditions
/// </summary>
[MethodImpl(MethodImplOptions.Synchronized)]
private void InvokeGridEvent(GridControlEventArgs e)
{
if (portfolioGridControl.InvokeRequired)
{
portfolioGridControl.Invoke(gridEvent, new object[] {e});
}
else
{
gridEvent(e);
}
}
}
}

GridControlEventArgs.cs is defined like this, and in my case it passes
along some useful arguments:

using System;

namespace JHalesCallBacks
{
public class GridControlEventArgs : EventArgs
{

public GridControlEventArgs(int aggrID, int assetID)
{
this.aggrID = aggrID;
this.assetID = assetID;
}

private int aggrID = 0;
public int AggrID
{
get { return aggrID;}
}

private int assetID = 0;
public int AssetID
{
get { return assetID;}
}

}
}

PortfolioGridControl.cs
Finally, in a User Control (PortfolioGridControl) that made use of the
GridCallBackEvent, I declared a class level instance that allows the
events to fire:

public class PortfolioGridControl : System.Windows.Forms.UserControl
{

private readonly GridCallBackEvent highlightPortfolioCallback;

public PortfolioGridControl()
{

// GridCallBackEvent wraps up all knowledge of getting a method
executed via threadpool
highlightPortfolioCallback = new GridCallBackEvent(this, new
GridCallBackEvent.GridEvent(HighlightEvent));
// add to centralised multicast handler
GridController.HighlightPortfolio += new
HighlightPortfolioHandler(highlightPortfolioCallback.InvokeEvent);
}

private void HighlightEvent(GridControlEventArgs e)
{
// This is the event that finally gets
called!
}
}

You can then eventually call highlightPortfolioCallback.InvokeEvent(new
GridControlEventArgs(aggrId, assetId)) to get HighlightEvent fired.

In my case, you can see that I added the definition for
highlightPortfolioCallback to a centralised multicast delegate in a
class called GridController which could fire groups of these events:
GridController.cs
using System;

namespace JHalesCallBacks
{
public class GridController
{

public static void FireHighlightPortfolio( int aggrId, int assetId)
{
if (HighlightPortfolio != null)
{
HighlightPortfolio(new GridControlEventArgs(aggrId, assetId));
}
}
public static event HighlightPortfolioHandler HighlightPortfolio;
}
}

Then it's just a case of calling
GridController.FireHighlightPortfolio() passing in the two args that it
requires

Hope that's not too convoluted!

Jason
 
Back
Top