Programming the Thread Pool in VB NET

The previous sections of the chapter dealt with theoretical aspects of using thread pools in the .NET Framework. Now it's time to cover the programmatic aspects of creating and using thread pools in .NET applications from a VB .NET perspective. As described in the previous section, the System.Threading namespace contains the ThreadPool class that you can use to create a thread pool in .NET applications.

Before we start coding, you need to know three important rules concerning the ThreadPool class:

• There can be only one working thread per ThreadPool object.

• There can be only one ThreadPool object per process.

• A ThreadPool object is created for the first time when you call the ThreadPool.QueueUserWorkItem() method, or when a callback method is called through a timer or registered wait operation.

One common use of the ThreadPool class is to start many separate tasks without setting the properties of each thread. The following console example (ThreadModule.vb) shows how to add the tasks to a queue and how a ThreadPool object assigns the threads for each task currently waiting:

Imports System Imports System.Threading

Module ThreadModule

Public Sub longtask1(ByVal obj As Object) Dim i As Integer For i = 0 To 999 Console.WriteLine( _

"Long Task 1 is being executed")

Next End Sub

Public Sub longtask2(ByVal obj As Object) Dim i As Integer For i = 0 To 999 Console.WriteLine( _

"Long Task 2 is being executed")

Next End Sub

Sub Main() Dim thrPool As ThreadPool thrPool.OueueUserWorkItem( _ New WaitCallback(AddressOf thrPool.OueueUserWorkItem( _ New WaitCallback(AddressOf Console.Read() End Sub End Module

Let's dissect the preceding example. It comprises two separate tasks called longtask1 and longtask2 that do the simple job of outputting a message to the console in a loop. A ThreadPool class can be employed to start these two tasks without setting the properties of threads for each individual task by passing the delegate of the procedure to the WaitCallback() method, as given by the following block of code:

Dim thrPool As ThreadPool thrPool.OueueUserWorkItem( _

New WaitCallback(AddressOf longtask1)) thrPool.OueueUserWorkItem( _

New WaitCallback(AddressOf longtask2))

The example also has a Console.Read() statement that holds the input on the console until the user presses the Enter key (or any other key).

longtask1)) longtask2))

The next example (ThreadAppModule.vb) shows how to pass and return values from a thread in a thread pool. Note that only Sub procedures can be queued to a ThreadPool, hence we cannot return values directly, as in the case of functions. However, we can wrap up all these parameters into a class and pass an instance of that class as an argument to the OueueUserWorkItem() method:

Imports System Imports System.Threading

Friend Class ObjState Friend inarg1 As String Friend inarg2 As String Friend outval As String End Class

Module ThreadAppModule

Sub Task1(ByVal StateObj As Object) Dim StObj As ObjState

'cast the parameter object to the type ObjState StObj = CType(StateObj, ObjState) Console.WriteLine( _

"Input Argument 1 in task 1:{0}", StObj.inarg1) Console.WriteLine( _

"Input Argument 2 in task 1:{0}", StObj.inarg2) 'The class variables can be employed 'for returning values StObj.outval = "From Task1 {0} {1}", _ StObj.inarg1, StObj.inarg2 End Sub

Sub Task2(ByVal StateObj As Object) Dim StObj As ObjState

'cast the parameter object to the type ObjState StObj = CType(StateObj, ObjState) Console.WriteLine( _

"Input Argument 1 in task 2:{0}", StObj.inarg1) Console.WriteLine( _

"Input Argument 2 in task 2:{0}", StObj.inarg2) 'The class variables can be employed for 'returning values

StObj.outval = String.Format("From Task2 {0} {1}", StObj.inarg1, StObj.inarg2) End Sub

Sub Main()

Dim TPool As System.Threading.ThreadPool Dim StObj1 As New ObjState() Dim StObj2 As New ObjState() ' Set some fields that act like parameters in ' the state object.

StObj1.inarg1 = "String Param1 of task 1"

StObj1.inarg2 = "String Param2 of task 1"

StObj2.inarg1 = "String Param1 of task 2"

StObj2.inarg2 = "String Param2 of task 2"

' Queue a task TPool.QueueUserWorkItem( _

New System.Threading.WaitCallback _ (AddressOf Task1), StObj1) ' Queue another task TPool.QueueUserWorkItem( _

New System.Threading.WaitCallback _ (AddressOf Task2), StObj2) Console.Read() End Sub

End Module

The output from ThreadAppModule is

Input Argument 1 in task 1: String Param1 of task 1

Input Argument 2 in task 1: String Param2 of task 1

Input Argument 1 in task 2: String Param1 of task 2

Input Argument 2 in task 2: String Param2 of task 2

Let's explore the preceding example step by step. This example is similar to the previous example except for the passing of an object; we are passing the input and output parameters to tasks queued in the thread pool using the ObjState object.

The ObjState object contains two input parameters and one output parameter, all of type String, as given by the following code block:

Friend Class ObjState Friend inarg1 As String Friend inarg2 As String Friend outval As String End Class

Next we define two procedures task1 and task2 and pass an instance of ObjState as a parameter to each of them. The procedures task1 and task2 concatenate the values of input parameters inarg1 and inarg2 of passed ObjState object and store the result in the outval class variable. This is given by the following code block:

Sub Task1(ByVal StateObj As Object) Dim StObj As ObjState

'cast the parameter object to the type ObjState StObj = CType(StateObj, ObjState) Console.WriteLine( _

"Input Argument 1 in task1 {0}", StObj.inarg1) Console.WriteLine( _

"Input Argument 2 in task1 {0}", StObj.inarg2)

'The class variables can be employed for 'returning values

StObj.outval = "From Taskl {)} {l}", _ StObj.inargl, StObj.inarg2 End Sub

Sub Task2(ByVal StateObj As Object) Dim StObj As ObjState

'cast the parameter object to the type ObjState StObj = CType(StateObj, ObjState) Console.WriteLine( _

"Input Argument 1 in task 2 {0}", StObj.inargl) Console.WriteLine( _

"Input Argument 2 in task2 {0}", StObj.inarg2) 'The class variables can be employed for 'returning values

StObj.outval = String.Format("From Task2 {0} {l}", _ StObj.inargl, StObj.inarg2) End Sub

In the Sub Main() method, we queue these two tasks in the thread pool employing the OueueUserWorkItem() method of the ThreadPool class, as given by the following code block:

Sub Main() Dim thrPool As ThreadPool thrPool.OueueUserWorkItem( _

New WaitCallback(AddressOf longtaskl)) thrPool.OueueUserWorkItem( _

New WaitCallback(AddressOf longtask2)) Console.Read() End Sub

We can also queue work items that have wait operations involved with them to the thread pool by employing the RegisterWaitForSingleObject() to which WaitHandle is passed as an argument. This WaitHandle signals the method wrapped in a WaitOrTimerCallback delegate. In this case, the thread pool creates a background thread to invoke the callback method. The following example (vbThreadPool.vb) demonstrates this concept:

Imports System Imports System.Threading

Public Class vbThreadPool Private Shared i As Integer = 0

Public Shared Sub Main() Dim arev As New AutoResetEvent(False) ThreadPool.RegisterWaitForSingleObject(arev, _ New WaitOrTimerCallback(AddressOf workitem), _ Nothing, 2000, False)

arev.Set() Console.Read() End Sub

Public Shared Sub workitem(ByVal O As Object, _ ByVal signaled As Boolean) i += 1

Console.WriteLine( _

"Thread Pool Work Item Invoked: {0}", i) End Sub End Class

The output from the preceding example is something like the following:

Thread

Pool

Work

Item

Invoked:

1

Thread

Pool

Work

Item

Invoked:

2

Thread

Pool

Work

Item

Invoked:

3

Thread

Pool

Work

Item

Invoked:

4

The output continues with a new line printed every 2 seconds and the value of i incremented by one until the user presses the Enter key to invoke the Console.Read() statement.

To start, an AutoResetEvent object called arev is created to signal the execution of queued work items:

Dim arev As New AutoResetEvent(False)

We invoke the RegisterWaitForSingleObject() method, which registers a delegate and signals the work item at the specified time interval. In our example, the interval is set to 2 seconds as given by the following piece of code:

ThreadPool.RegisterWaitForSingleObject(arev, _ New WaitOrTimerCallback(AddressOf workitem), _ Nothing, 2000, False)

To raise the event, we need to use the set() method of the AutoResetEvent object:

arev.Set()

This example concludes the practical session on using thread pools in VB .NET applications. The next section examines scalability and builds a thread pool manager application.

+1 0

Post a comment