Thursday, August 6, 2009

Getting data back from a thread

How do you get data back from a thread if you don't know when it'll finish running? You do it by using a delegate. When the code in the thread is complete, it'll call a delegate that is referenced back at the originating code.

Here are the basic steps:
  1. Define a delegate.
  2. public delegate void ReturnResult(object sender);
  3. In the class that contains the method to be called, create a field/property of your new delegate.
  4. public ReturnResult ReturnResultDelegate;
  5. In the class that starts the thread, define a method that matches your delegate's signature. This is the method that will be receiving the callback from the thread.
  6. static void CheckReturnedResult(object sender)
    {
        Math m = (Math) sender;
        Console.WriteLine("Result:" + m.Result);
    }
  7. Instantiate the class and set your delegate to the method you created.
  8. Math math = new Math();
    math.Value1 = 1;
    math.Value2 = 3;
    math.ReturnResultDelegate = CheckReturnedResult;
  9. Have your class call the delegate somewhere when the thread runs.
  10. public void Add(object o)
    {
        Result = Value1 + Value2;
        ReturnResultDelegate(this);
    }
Optionally, you can send just the return result back instead of the whole class.
Let's put everything together and see how it all works in this console application:
using System;
using System.Threading;

namespace ThreadingApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Math math = new Math();
            math.Value1 = 1;
            math.Value2 = 3;
            math.ReturnResultDelegate = CheckReturnedResult;

            ThreadPool.QueueUserWorkItem(math.Add);
            Thread.Sleep(1000);
        }

        static void CheckReturnedResult(object sender)
        {
            Math m = (Math)sender;
            Console.WriteLine("Result:" + m.Result);
        }
    }

    public class Math
    {
        public int Value1;
        public int Value2;
        public int Result;
        public delegate void ReturnResult(object sender);

        public ReturnResult ReturnResultDelegate;

        public void Add(object o)
        {
            Result = Value1 + Value2;
            ReturnResultDelegate(this);
        }
    }
}
In this sample, I minimize the scope of the delegate by including it in the class being called (Math). You might declare it outside the class if needed.