Browse DevX
Sign up for e-mail newsletters from DevX


How To Pass Parameters to Threads in Windows Forms Applications—and Get Results : Page 5

Launching new threads is easy, but it's not as obvious how you can pass parameters to the threads and get the results back. In addition, accessing Windows Forms controls from multiple threads can cause problems. In this article, you'll see how to pass parameters, receive results, and access controls safely from multiple threads in a Windows Forms application.




Building the Right Environment to Support AI, Machine Learning and Deep Learning

Returning Values from Launched Threads
You've seen how to pass parameters to a launched thread; but what if you want to return the results—for example, suppose the thread calculates a total value and you need to know that value in the main thread? To do that you'll need to know when the thread exits and you'll need to be able to access the total value.

The sample Form4.vb contains the code to retrieve the total value. First, alter the Count() method so it creates a total value and returns the total when the loop completes. You need to alter the StartCountingDelegate to match the new signature as well. The boldface lines in the following code show the changes.

Delegate Function StartCounterDelegate( _ ByVal iterations As Integer) As Integer Public Function Count(ByVal iterations As Integer) _ As Integer Dim i As Integer Dim t As Thread = Thread.CurrentThread Dim total As Integer For i = 0 To iterations total += i If Me.txtDisplay.InvokeRequired Then Me.txtDisplay.Invoke( _ New ChangeTextControlDelegate( _ AddressOf SetDisplayText), New Object() _ {t.Name, txtDisplay, i.ToString}) Else Me.SetDisplayText(t.Name, _ txtDisplay, i.ToString) End If If Me.txtDisplay.InvokeRequired Then t.Sleep(100) End If Next If Me.txtDisplay.InvokeRequired Then Me.txtDisplay.Invoke( _ New ChangeTextControlDelegate( _ AddressOf SetDisplayText), New Object() _ {t.Name, txtDisplay, "Ending Thread"}) Else Me.SetDisplayText(t.Name, txtDisplay, _ "Ending Thread") End If Return total End Function

The thread launches in the context of the CounterArgs class, so you can get the return value there, but more typically, you'd want the CounterArgs class to notify the main form thread when the count is done and the results are available. The answer is to create an event and raise it in the CounterArgs class when the Count() method returns. The DoneCounting event passes the current thread name and the total to the event handler. Although you can pass anything you want as event arguments, the .NET convention is to pass the object invoking the event as the first argument and pass an EventArgs, or a class derived from EventArgs as the second argument. The DoneCountingEventArgs class inherits from EventArgs and contains the data you need to send.

Private Class DoneCountingEventArgs Inherits EventArgs Private m_threadName As String Private m_total As Integer Public Sub New(ByVal threadName As String, ByVal total As Integer) m_threadName = threadName m_total = total End Sub Public ReadOnly Property ThreadName() As String Get Return m_threadName End Get End Property Public ReadOnly Property Total() As Integer Get Return m_total End Get End Property End Class

Here are the alterations you need to make to the CounterArgs class.

Private Class CounterArgs Public Event DoneCounting( _ ByVal sender As Object, _ ByVal e As DoneCountingEventArgs) Public iterations As Integer Public startDelegate As StartCounterDelegate Public Sub StartCounting() Dim total As Integer total = startDelegate(iterations) Dim args As New DoneCountingEventArgs(Thread.CurrentThread.Name, total) RaiseEvent DoneCounting(Me, args) End Sub End Class

Next you need an event-handler in the main form to handle the DoneCounting event and display the total:

Private Sub Done(ByVal object As Sender, _ ByVal e As DoneCountingEventArgs) Console.WriteLine(e.ThreadName & ": Total=" & _ e.Total.ToString) End Sub

Then, wire the event to the new handler in the btnLaunchThread_Click code:

AddHandler ca.DoneCounting, AddressOf Done

That's it. When you run Form 4, you'll see the thread name and total appear in the Output window.

One final note: The Done() method is defined in the main form, but the CounterArgs class raises the DoneCounting event from the launched thread. When that event fires, it causes the Done() method to execute. Which thread do you think the Done() method executes on, the main form thread, or the launched thread? Add this code to the Done() method to find out.

If txtDisplay.InvokeRequired Then Console.Write("Done is executing on the " & _ "launched thread.") Else Console.Write("Done is executing on the " & _ "main form thread.") End If

When you add the preceding code and run Form 4, you'll see that the event-handler runs in the context of the thread that raised the event. Therefore, you have to be careful how you access controls in event-handlers that might be fired by launched threads

Another way to perform the same task is to create an EndCounterDelegate with the same signature as the Done() method. Add a public field of type EndCounterDelegate to the CounterArgs class, set the delegate's method to the Done() method, and call it when the Count() method completes. As both the concept and the code is nearly identical to the event-raising example in Form 4, I won't show it here, but you can find it in the downloadable sample code in Form 5. Note that you must check IsInvokeRequired using this technique as well.

To sum up, to pass parameters to threads, you need to launch them in the context of a class that has access to the parameter values. To return values created during the execution of a launched thread, raise events or use a delegate to pass the values to a specific method when the thread exits. If you need to access controls created on the main form thread during thread execution, always use the InvokeRequired property to check whether you need to use the control's Invoke() method to access its data.

Russell Jones is DevX's Executive Editor. He's a former reptile keeper and professional musician who now composes computer applications. His most recent books are Mastering ASP.NET with VB.NET and Mastering ASP.NET with Visual C# (both published by Sybex). Reach him by e-mail .
Thanks for your registration, follow us on our social networks to keep up-to-date