Supplying and returning values in a multithreaded application is complicated because the constructor for the thread class must be passed a reference to a procedure that takes no arguments and returns no value.
Parameters for Multithreaded Procedure
The best way to supply parameters for a multithreaded method call is to wrap the target method in a class and define fields for that class that will serve as parameters for the new thread. The advantage of this approach is that you can create a new instance of the class, with its own parameters, every time you want to start a new thread. For example, suppose you have a function that calculates the area of a triangle, as in the following code:
public class AreaClass { public double Base; public double Height; public double Area; public void CalcArea() { Area = 0.5 * Base * Height; MessageBox.Show("The area is: " + Area.ToString()); } } protected void TestArea() { AreaClass AreaObject = new AreaClass(); System.Threading.Thread Thread = new System.Threading.Thread(AreaObject.CalcArea); AreaObject.Base = 30; AreaObject.Height = 40; Thread.Start(); }
Returning values from Multithreaded Procedure
Returning values from procedures that run on separate threads is complicated by the fact that the procedures cannot be functions and cannot use ByRef arguments. The easiest way to return values is to use the BackgroundWorker component to manage your threads and raise an event when the task is done, and process the results with an event handler.
The following example returns a value by raising an event from a procedure running on a separate thread:
class AreaClass2 { public double Base; public double Height; public double CalcArea() { // Calculate the area of a triangle. return 0.5 * Base * Height; } } private System.ComponentModel.BackgroundWorker BackgroundWorker1 = new System.ComponentModel.BackgroundWorker(); private void TestArea2() { InitializeBackgroundWorker(); AreaClass2 AreaObject2 = new AreaClass2(); AreaObject2.Base = 30; AreaObject2.Height = 40; // Start the asynchronous operation. BackgroundWorker1.RunWorkerAsync(AreaObject2); } private void InitializeBackgroundWorker() { // Attach event handlers to the BackgroundWorker object. BackgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(BackgroundWorker1_DoWork); BackgroundWorker1.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(BackgroundWorker1_RunWorkerCompleted); } private void BackgroundWorker1_DoWork( object sender, System.ComponentModel.DoWorkEventArgs e) { AreaClass2 AreaObject2 = (AreaClass2)e.Argument; // Return the value through the Result property. e.Result = AreaObject2.CalcArea(); } private void BackgroundWorker1_RunWorkerCompleted( object sender, System.ComponentModel.RunWorkerCompletedEventArgs e) { // Access the result through the Result property. double Area = (double)e.Result; MessageBox.Show("The area is: " + Area.ToString()); }