Scrigroup - Documente si articole

     

HomeDocumenteUploadResurseAlte limbi doc
AccessAdobe photoshopAlgoritmiAutocadBaze de dateCC sharp
CalculatoareCorel drawDot netExcelFox proFrontpageHardware
HtmlInternetJavaLinuxMatlabMs dosPascal
PhpPower pointRetele calculatoareSqlTutorialsWebdesignWindows
WordXml

AspAutocadCDot netExcelFox proHtmlJava
LinuxMathcadPhotoshopPhpSqlVisual studioWindowsXml

Programming the Thread Pool in C#

c



+ Font mai mare | - Font mai mic



Programming the Thread Pool in C#

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



Before we start coding, there are two important rules that we must be clear about concerning the ThreadPool class. They are:

There can be only one ThreadPool object per application domain

A ThreadPool object is created for the first time when we call the ThreadPool.QueueUserWorkItem() method, or when a callback method is called through a timer or registered wait operation (which use the application domain thread pool internally)

First, let's see through examples how a ThreadPool class can be beneficial over starting individual Threads. In the next example (ThreadDemo.cs) we will use independent Threads to start two long tasks and in the following example (ThreadPoolDemo.cs), we will start the same two tasks, but using a ThreadPool:

using System;
using System.Threading;

class ThreadDemo



public void LongTask2()



static void Main()


Console.Read();


In the above example, we start two separate tasks LongTask1 and LongTask2 using independent threads t1 and t2. Note that we call threads repeatedly, in a loop, to stress the processing power of the operating system, so that we can get a better view of the benefits of using a ThreadPool. The ThreadPoolDemo class below shows the usage of the ThreadPool class.

using System;
using System.Threading;

class ThreadPoolDemo


public void LongTask2(object obj)



static void Main()


Console.Read();


Let's dissect the above 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:

ThreadPool.QueueUserWorkItem(new WaitCallback(tpd.LongTask1));
ThreadPool.QueueUserWorkItem(new WaitCallback(tpd.LongTask2));

Note that the QueueUserWorkItem is a static method in the ThreadPool class and hence can be called directly by the class (ThreadPool). The example also has a Console.Read() statement, which holds the input on the console until the user presses the Enter key (or any other key).

By running the ThreadDemo and ThreadPoolDemo applications (separately, one after the other), we can compare the thread usage using the Windows Task Manager. The numbers on each operating system will be different depending on the power of the operating system, but relatively, results will be the same.

Threads usage of ThreadDemo application:

Thread usage of the ThreadPoolDemo application:

By comparing these screenshots, it is evident that using a ThreadPool not only helps reduce the thread usage of the application, but also reduces the CPU time, as well as the memory used by the application.

The next example (ThreadPoolState.cs) shows how to pass and return values from a thread in a thread pool. The thread pool framework only allows us to pass a single object parameter, but often we may want to pass several parameters to the method that we wish to be executed by a thread from the pool. However, we can easily wrap up all of the necessary parameters into a class and can pass an instance of that class as an argument to the QueueUserWorkItem() method:

using System;
using System.Threading;

class ObjState


class ThreadPoolState


public void Task2(object stateObj)


static void Main()


The output from ThreadPoolState will be:

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 above example step by step. This example is pretty 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:

internal class ObjState

Next we define two methods, 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 the input parameters inarg1 and inarg2 of the passed ObjState object and store the result in the outval class variable. This is given by the following code block:

public void Task1(object stateObj)


public void Task2(object stateObj)

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

static void Main()

We can also queue work items that have wait operations involved with them to the thread pool by employing 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 (RegWait.cs) demonstrates this concept:

using System;
using System.Threading;

public class RegWait

public static void workitem(object O ,bool signaled)


The output from the above example will be something like:

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 will continue 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 with an initial state of non-signaled to signal the execution of queued work items:

AutoResetEvent arev = new AutoResetEvent(false);

We invoke the RegisterWaitForSingleObject() method with null for the state parameter, 2000 milliseconds as the timeout value and false for the executeOnceOnly parameter. RegisterWaitForSingleObject() registers a delegate and signals the work item at the specified time interval. In our example, it is set to 2 seconds as given by the following piece of code:

ThreadPool.RegisterWaitForSingleObject
(arev, new WaitOrTimerCallback(workitem), null, 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 C# applications; in the next section we will examine scalability and build a thread pool manager application.

A Multi-Threaded Microsoft Message Queue (MSMQ) Listener

In this section, we will leverage the ThreadPool to send and listen to the messages from MSMQ. MSMQ is a distributed queue in which one application can send messages to another application asynchronously.

In a typical scenario, we have an MSMQ server that maintains the list of queues we want to send messages to. MSMQ Senders make a connection to the MSMQ Server (to a particular queue) and send messages to that queue. An MSMQ receiver receives messages sent by the MSMQ Senders. The MSMQ Receiver has to listen on a specific queue to receive messages sent on that queue. Thus, the MSMQ Server acts as a broker between the MSMQ Sender(s) and the MSMQ Receiver. The MSMQ Sender is unaware of the MSMQ Receiver and vice-versa.

In our application, we will develop an MSMQ Sender Windows Forms Application and an MSMQ Receiver as a Console application. In our MSMQ Sender Application (MSMQUI.cs), we send the messages to the queue using a ThreadPool:

int count = Convert.ToInt32(countTxt.Text);
while(count > 0)

In the above code snippet (from MSMQUI.cs), we create a counter to send messages to the MSMQ. We put the messages into the ThreadPool as the state object and give a reference of the SendMessage() method to the WaitCallback delegate. SendMessage() is the method that will be called by the ThreadPool to send the message. In other words, the SendMessage() method will do the actual job of sending the message to the MSMQ.

public void SendMessage(object state)

The SendMessage() method converts the state object to string and send it to the MSMQ.

The MSMQUI application looks like the following screenshot when executed.

In the MSMQ Receiver (MSMQListener.cs), we receive the message notification from the MSMQ Server asynchronously whenever a message arrives into the queue. For this reason, we create the MSMQ Event Handler and pass the MessageReceived() method name to the ReceiveCompletedEventHandler delegate.

this._mq1.ReceiveCompleted += new
System.Messaging.ReceiveCompletedEventHandler(this.MessageReceived);

To start receiving messages from MSMQ, we need to call the BeginReceive() method on the message queue object.

_mql.BeginReceive(new TimeSpan(0,0,2));

The BeginReceive() method takes a TimeSpan object to indicate how long the Receiver should wait till the message arrives into the queue. If the TimeSpan is not supplied, the application will block on the BeginReceive() method till a message arrives into MSMQ.

The MessageReceived() method is called by the MSMQ event handler when a message arrives into the queue:

public void MessageReceived(object source,
System.Messaging.ReceiveCompletedEventArgs asyncResult)

catch(MessageQueueException)

catch(Exception ex)

finally



static void InvokeMDAO(object state)

To get the arrived message, we have to call the EndReceive() method on the message queue object. After we get the System.Messaging.Message object, it is up to us to do whatever processing we want on to this object. Typically in a real-world application, we would call System.EnterpriseServices (COM+) object and that would in-turn update some database or forward the message to some other object for processing. Here we can use the ThreadPool to keep the object invocation under control. We put the body of the message into the ThreadPool and pass the name of the InvokeMDAO() method to the WaitCallback delegate. The InvokeMDAO() method invokes a method on the object and prints message on the console. As discussed earlier, we can invoke a System.EnterpriseServices (COM+) component in the InvokeMDAO() method. This would indirectly control the number of created COM+ objects, which would be directly proportional to the numbers of threads created by the ThreadPool. Finally, we have to call the BeginReceive() method again to restart the async wait. If this method is not called, the message receiver stops receiving messages from the MSMQ.



Politica de confidentialitate | Termeni si conditii de utilizare



DISTRIBUIE DOCUMENTUL

Comentarii


Vizualizari: 1001
Importanta: rank

Comenteaza documentul:

Te rugam sa te autentifici sau sa iti faci cont pentru a putea comenta

Creaza cont nou

Termeni si conditii de utilizare | Contact
© SCRIGROUP 2024 . All rights reserved