Click here to Skip to main content
15,881,821 members
Articles / Programming Languages / C#
Article

Action Extensions

Rate me:
Please Sign up or sign in to vote.
4.95/5 (18 votes)
1 Jul 2008CPOL 39.4K   222   25   8
Parallelization of multicast delegates

Introduction

Ennis Ray Lynch, Jr. gave me an idea the other day in the Lounge. The basic idea was running multicast delegates in parallel.

Background

The code to run delegates in parallel is rather trivial.

C#
static void Run(Action[] aa)
{
  List<IAsyncResult> waits = new List<IAsyncResult>();

  foreach (Action a in aa)
  {
    waits.Add(a.BeginInvoke(null, null));
  }

  foreach (IAsyncResult ar in waits)
  {
    ar.AsyncWaitHandle.WaitOne();
  }
}

To run a multicast delegate in parallel follows the same pattern.

C#
static void Run(Action t)
{
  List<IAsyncResult> waits = new List<IAsyncResult>();

  foreach (Action a in t.GetInvocationList())
  {
    waits.Add(a.BeginInvoke(null, null));
  }

  foreach (IAsyncResult ar in waits)
  {
    ar.AsyncWaitHandle.WaitOne();
  }
}

Using the Code

Following the above pattern, we simply create stubs for the generic Action delegate (only 1 shown for clarity). If needed, you can replace with your own delegate type. There is one important aspect to keep in mind; the delegate MUST return void. Why, you may ask? The answer is simple. There is no easy way to consume multiple return values in C#.

C#
static class ActionExtensions
{
  class WaitList : IDisposable
  {
    readonly List<IAsyncResult> waits = new List<IAsyncResult>();

    public void Add(IAsyncResult ar)
    {
      waits.Add(ar);
    }

    public void Dispose()
    {
      foreach (var ar in waits)
      {
        ar.AsyncWaitHandle.WaitOne();
      }
    }
  }

  public static Action MakeParallel(this Action t)
  {
    return () =>
    {
      using (var w = new WaitList())
      {
        foreach (Action a in t.GetInvocationList())
        {
          w.Add(a.BeginInvoke(null, null));
        }
      }
    };
  }
}

The usage is also trivial. Simply call the MakeParallel extension method.

C#
class Program
{
  static void Main(string[] args)
  {
    Action<int> f = null;

    for (int i = 0; i < 8; i++)
    {
      f += Thread.Sleep;
    }

    Stopwatch ws = Stopwatch.StartNew();

    f(250);

    Console.WriteLine("ser: {0:f3}", ws.Elapsed.TotalMilliseconds);

    f = f.MakeParallel();

    ws = Stopwatch.StartNew();

    f(250);

    Console.WriteLine("par: {0:f3}", ws.Elapsed.TotalMilliseconds);

    Console.ReadLine();
  }
}

The output should show the parallel version running at half (or quarter) the time of the serial version.

Points of Interest

From what I can determine, BeginInvoke utilizes the number of logical CPUs. I have however not been able to test this.

History

  • 2nd July, 2008 - Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
South Africa South Africa
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralNice work Pin
kin3tik2-Jul-08 5:42
kin3tik2-Jul-08 5:42 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.