WriteLine("Incrementer: {0}", i);
}
}
and one that counts down from 10:
public void Decrementer( )
{
for (int i = 10;i>=0;i--)
page 421
Programming C#
{
Console.WriteLine("Decrementer: {0}", i);
}
}
To run these in threads, you create two new threads, each initialized with a ThreadStart delegate, which in turn would be initialized to the respective member functions:
Thread t1 = new Thread( new ThreadStart(Incrementer) );
Thread t2 = new Thread( new ThreadStart(Decrementer) );
Instantiating these threads does not start them running. To do so you must call the Start method on the Thread object itself:
t1.Start( );
t2.Start( );
If you don't take further action, the thread will stop when the function returns.
You'll see how to stop a thread before the function ends later in this chapter.
Example 20-1 is the full program and its output. You will need to add a using statement for System.Threading to make the compiler aware of the Thread class. Notice the output, where you can see the processor switching from t1 to t2.
Example 20-1. Using threads
namespace Programming_CSharp
{
using System;
using System.Threading;
class Tester
{
static void Main( )
{
// make an instance of this class
Tester t = new Tester( );
// run outside static Main
t.DoTest( );
}
public void DoTest( )
{
// create a thread for the Incrementer
// pass in a ThreadStart delegate
// with the address of Incrementer
Thread t1 =
new Thread(
new ThreadStart(Incrementer) );
// create a thread for the Decrementer
// pass in a ThreadStart delegate
// with the address of Decrementer
Thread t2 =
new Thread(
new ThreadStart(Decrementer) );
// start the threads
page 422
Programming C#
t1.Start( );
t2.Start( );
}
// demo function, counts up to 1K
public void Incrementer( )
{
for (int i =0;i<1000;i++)
{
Console.WriteLine(
"Incrementer: {0}", i);
}
}
// demo function, counts down from 1k
public void Decrementer( )
{
for (int i = 1000;i>=0;i--)
{
Console.WriteLine(
"Decrementer: {0}", i);
}
}
}
}
Output:
Incrementer: 102
Incrementer: 103
Incrementer: 104
Incrementer: 105
Incrementer: 106
Decrementer: 1000
Decrementer: 999
Decrementer: 998
Decrementer: 997
The processor allows the first thread to run long enough to count up to 106. Then, the second thread kicks in, counting down from 1000 for a while, and then the first thread is allowed to run. When I run this with larger numbers, I notice that each thread is allowed to run for about 100 numbers before switching. The actual amount of time devoted to any given thread is handled by the thread scheduler and will depend on many factors, such as the processor speed, demands on the processor from other programs, and so forth.
20.1.2 Joining Threads
When you tell a thread to stop processing and wait until a second thread completes its work, you are said to be joining the first thread to the second. It is as if you tied the tip of the first thread on to the tail of the second?hence "joining" them.
To join thread 1 (t1) onto thread 2 (t2), you write:
t2.Join( );
If this statement is executed in a method in thread t1, t1 will halt and wait until t2 completes and exits. For example, we might ask the thread in which Main( ) executes to wait for all our other threads to end before it writes its concluding message. In this next code snippet, assume you've page 423
Programming C#
created a collection of threads named myThreads. You will iterate over the collection, joining the current thread to each thread in the collection in turn:
foreach (Thread myThread in myThreads)
{
myThread.Join( );
}
Console.WriteLine("All my threads are done.");
The final message All my threads are done will not be printed until all the threads have ended.
In a production environment, you might start up a series of threads to accomplish some task (e.g., printing, updating the display, etc.) and not want to continue the main thread of execution until the worker threads are completed.
20.1.3 Suspending Threads
At times, you want to suspend your thread for a short while. You might, for example, like your clock thread to suspend for about a second in between testing the system time. This lets you display the new time about once a second without devoting hundreds of millions of machine cycles to the effort.
The Thread class offers a public static method, Sleep, for just this purpose. The method is overloaded; one version takes an int, the other a timeSpan object. Each represents the number of milliseconds you want the thread suspended for, expressed either as an int (e.g., 2000 = 2000
milliseconds or two seconds) or as a timeSpan.
Although timeSpan objects can measure ticks (100 nanoseconds), the Sleep( ) method's granularity is in milliseconds (1,000 nanoseconds).
To cause your thread to sleep for one second, you can invoke the static method of Thread, Sleep, which suspends the thread in which it is invoked:
Thread.Sleep(1000);
At times, you'll tell your thread to sleep for only one millisecond. You might do this to signal to the thread scheduler that you'd like your thread to yield to another thread, even if the thread scheduler might otherwise give your thread a bit more time.
If you modify Example 20-1 to add a Thread.Sleep(1) statement after each WriteLine, the output changes significantly:
for (int i =0;i<1000;i++)
{
Console.WriteLine(
"Incrementer: {0}", i);
Thread.Sleep(1);
}
This small change is sufficient to give each thread an opportunity to run once the other thread prints one value. The output reflects this change:
|