100 Chapter 4: Beyond the Basics
■
The .NET API provides System.Threading class library for implementing threads. The
.NET threading capabilities are very flexible and allow a program to handle many network
connections simultaneously. Using threads, a single application can work on several tasks
concurrently. In our echo server, we can give responsibility for each client to an indepen-
dently executing thread. All of the examples we have seen so far consist of a single thread,
which simply executes the Main() method. In this section we describe two approaches to
coding concurrent servers, namely, thread-per-client, where a new thread is spawned to
handle each client connection, and thread pool, where a fixed, prespawned set of threads
work together to handle client connections.
To create a new thread in C# you create a new instance of the Thread class, which
as its argument takes a delegate method that will operate in its own thread. This thread
delegate is represented by the ThreadStart class, which takes the method to be run as its
argument. Once the Thread has been instantiated, the Start() method is called to begin
execution on that thread. For example, if you have created a method called runMyThread(),
the code to create and start the code running as its own thread would be:
using System.Threading;
:
:
:
// Create a ThreadStart instance using your method as a delegate:
ThreadStart methodDelegate = new ThreadStart(runMyThread);
// Create a Thread instance using your delegate method:
Thread t = new Thread(methodDelegate);
// Start the thread
t.Start();
The new thread does not begin execution until its Start() method is invoked. When
the Start() method of an instance of Thread is invoked, the CLR causes the specified
method to be executed in a new thread, concurrently with all others. Meanwhile, the
original thread returns from its call to Start() and continues its execution independently.
(Note that directly calling the method without passing it to a Thread via a delegate has the
normal procedure-call semantics: the method is executed in the caller’s thread.) The exact
interleaving of thread execution is determined by several factors, including the implemen-
tation of the CLR, the load, the underlying OS, and the host configuration. For example,
on a uniprocessor system, threads share the processor sequentially; on a multiprocessor
system, multiple threads from the same application can run simultaneously on different
processors.
Note that the method delegate cannot take any arguments or return a value. Luckily,
there are mechanisms to circumvent both of these limitations. To pass arguments into
a Thread instance while maintaining data encapsulation, you could break your separate
thread code into its own class. For example, suppose you want to pass an instance of
TcpClient into your runMyThread() method. You could create a new class (e.g., MyThread-
Class) that contained the runMyThread() method, and pass the TcpClient instance into