codeflood logo

Multithreading with Delegates

Multithreaded code is becoming more common place. Even for web applications. This is largely due to the increasing number of cores appearing on CPUs these days. The chip manufacturers have come close to the limit in terms of how fast a chip can run before it starts to melt. So to continue increases in processing speed, instead of continuing to try and increase speed vertically by increasing chip speed, they have started to increase speed horizontally by increasing the number of cores on each chip. To make use of this increased processing power, we need to make sure our code can run multiple threads at a time, as each CPU can only work on a single thread at a time.

In .net we have many ways in which we can code to enable our application to run multiple threads at once. The first technique we all normally use is the Thread class. This class allows us to explicitly control a thread along with it's execution and lifetime. To start a new thread we can use the following:

ThreadStart threadStart = new ThreadStart(Method);
Thread thread = new Thread(threadStart);
thread.Start();

This code will execute a method on the current class in another thread. To block the calling thread until the new thread completes I can use:

thread.Join();

The method in the above case must return void and cannot accept any parameters. If we want to pass parameters then we can use a ParameterizedThreadStart instead of the ThreadStart above.

MyClass data = new MyClass();
data.Prop1 = "data1";
data.Prop2 = 2;

var threadStart = new ParameterizedThreadStart(Method);
Thread thread = new Thread(threadStart);
thread.Start(data);

The Method should still return void but now must accept a single parameter of type object.

void Method(object data) { }

But what if I want to pass multiple parameters and perhaps even accept a return value? For multiple parameters I could create a custom class with a property for each parameter we wish to pass. But I still wouldn't be able to return a value from the thread. Instead I can use delegates.

Delegates are essentially method pointers that point to the method we want to run. This is how .net does event handling. When I subscribe to the Click event of a button, I'm passing to the event a delegate which points to the method I want to run when that event occurs. Delegates have asynchronous programming capabilities built in through the use of the BeginInvoke and EndInvoke methods. Being asynchronous, each invocation is run on a separate new thread.

I'll start by defining my delegate with the parameters I want to pass and the return type I require.

delegate int AsyncMethod(int num1, int num2, string message);

Now I create an instance of my delegate and begin the invocation asynchronously.

AsyncMethod myMethod = new AsyncMethod(Method);
IAsyncResult result = myMethod.BeginInvoke(4, 6, "my message", null, null);

And the method that is invoked:

static int Method(int num1, int num2, string message)
{
  return num1 + num2;
}

We use the IAsyncResult object returned from BeginInvoke to identify the instance of the invocation and to block the calling thread and get the return result.

At this point the new thread has been created and is running in the background. I can block the calling thread by calling EndInvoke on the delegate which is also how I get the return value from the execution.

int res = myMethod.EndInvoke(result);

If I have multiple threads which I've created and want to wait for them all to finish, I can use the WaitHandle.WaitAll method and pass it the array of WaitHandles (which I can get from IAsyncResult.AsyncWaitHandle) that I wish to wait for.

delegate int NumMethod(int data);

int Method(int data)
{
  return -1;
}

void Process()
{
  int[] inputData = PartitionData();
  NumMethod myMethod = new NumMethod(Method);
  WaitHandle[] handles = new WaitHandle[inputData.Length];
  for(int i = 0; i < inputData.Length; i++)
  {
    IAsyncResult result = myMethod.BeginInvoke(inputData[i],
      null, null);
    handles[i] = result.AsyncWaitHandle;
  }

  WaitHandle.WaitAll(handles);
}

One drawback of the delegate technique is that we can no longer control the lifetime of the thread that is running our code. This may or may not be a problem.

Comments

Ahmed

Excellent Article! Thanks,Ahmed

Leave a comment

All fields are required.