ONDotNet.com    
 Published on ONDotNet.com (http://www.ondotnet.com/)
 See this if you're having trouble printing code examples


C# News Ticker Multithreaded Application

by Budi Kurniawan
10/14/2002

Writing multithreaded applications is one of the more advanced topics in computer programming. Fortunately, the .NET Framework makes it a piece of cake by hiding the complexity in the classes in the System.Threading framework. This article shows you how to create a news ticker by using the System.Threading.Thread class. Its main purpose is to demonstrate how easy multithreaded programming can be.

Creating a News Ticker Using the System.Threading.Thread Class

A program can allocate processor time to units in its body. Each unit is then given a portion of the processor time. Even if your computer has only one processor, it can have multiple units that work at the same time. The trick is to slice processor time and give each slice to each processing unit. The smallest unit that can take processor time is called a thread. A program that has multiple threads is referred to as a multithreaded application. Examples of such programs are computer games, animation, and news tickers.

In the context of the .NET Framework, threads are the basic unit to which an operating system allocates processor time, and more than one thread can be executing code inside a process. Each thread maintains exception handlers, a scheduling priority, and a set of structures the system uses to save the thread context until it is scheduled. The thread context includes all of the information the thread needs to seamlessly resume execution, including the thread's set of CPU registers and stack, in the address space of the thread's host process.

With the .NET Framework, developing multithreaded applications cannot be easier. The class library provides the System.Threading namespace that contains classes that support multithreading. The central class in this namespace is Thread, which represents a thread. This is the topic of the next discussion.

The System.Threading.Thread Class

The System.Threading.Thread class has one constructor whose signature is as follows:


public Thread ( ThreadStart start );

To instantiate a Thread object, you pass a System.Threading.ThreadStart. ThreadStart is a delegate that is used to specify the program code executed by the Thread object. ThreadStart has the following signature.

Related Reading

Programming C#
By Jesse Liberty


public delegate void ThreadStart();

For example, to assign a method called Run to a Thread, you construct the Thread object using the following statement:


Thread myThread = new Thread(new ThreadStart(Run));

And then, to start the thread, you call its Start method.


myThread.Start();

In this case, after you call the Start method, the code in the Run method will be executed.

The Start method is an asynchronous method, therefore it returns immediately. A Thread object can have many possible states. Understanding these states is crucial to properly work with Thread objects. The possible states for a Thread object are members of the System.Threading.ThreadState enumeration. They are summarized in Table 1:

Table 1: Members of the System.Threading.ThreadState enumeration

MemberDescription
AbortedThe thread is in the Stopped state.
AbortRequestedThe ThreadAbort method has been invoked on the thread, but the thread has not yet received the pending System.Threading.ThreadAbortException that will attempt to terminate it.
BackgroundThe thread is being executed as a background thread, as opposed to a foreground thread. This state is controlled by setting the IsBackground property of the Thread class.
RunningThe thread has been started, it is not blocked, and there is no pending ThreadAbortException.
StoppedThe thread has stopped.
StopRequestedThe thread is being requested to stop. This is for internal use only.
SuspendedThe thread has been suspended.
SuspendRequestedThe thread is being requested to suspend.
UnstartedThe Thread.Start method has not been invoked on the thread.
WaitSleepJoinThe thread is blocked as a result of a call to Wait, Sleep or Join methods.

When first created, a thread is in the "Unstarted" state. After the Start method is called, the thread's state will become Running. A thread can be suspended by calling the Thread class's Suspend method. After the Suspend method is called, the thread will be in the SuspendRequested state. A suspended thread is resumed when the Resume method of the Thread class is called. When a thread is resumed, its state gets back to the Running state. The Sleep method is often used. As the name implies, it is used to send a thread to sleep for a specified period of time. After the time elapses, you don't have to wake up the thread because it will continue running automatically.

A Thread object should be terminated when the application exits. To do this, you use the Abort method. When this method is invoked, the system throws a System.Threading.ThreadAbortException in the thread to abort it. Calling the Abort method does not guarantee that the thread aborts immediately or at all. If a thread does an unbounded amount of computation in the final block called as part of the abort procedure, for example, the thread will be delayed from being aborted indefinitely. To make sure a thread has aborted, call the Join method after calling Abort. The Thread class has the IsAlive property that you can use to inquire about the state of a Thread object. If IsAlive property is True, the thread has been started and has not been aborted.

A thread can also run in background or in foreground. A background thread is the same as a foreground thread, except that background threads do not prevent a process from terminating. You can make a thread a background thread by setting the Thread class's IsBackground property to True.

Using the Thread Class in a Ticker Application

To illustrate the use of the Thread class in a multithreaded application, consider the example in Listing 1. The example features a form with two Label controls. We use two Thread objects (thread1 and thread2) to turn the labels into news tickers. The first ticker flashes three news items in the newsItems array of strings. The second ticker features four items in the businessItems array of strings.

Listing 1: The News Tickers Example


using System;
using System.Windows.Forms;
using System.ComponentModel;
using System.Drawing;
using System.Threading;
using System.IO;


public class Ticker : Form {

  Label label1, label2;

  String[] newsItems = 
    { "Safest Aerobic Machine Launched",
      "First Dog Cloning Is Only Days Away",
      "Reviving the Extinct Tasmanian Tiger" };

  String[] businessItems = 
    { "FirstMeasure Software to Go Nasdaq",
      "MFMF Directors To Meet For The First Time",
      "First Sign of Economic Recovery Finally At Sight",
      "Euro Hits Record Low (Again)" };
  
  Thread thread1, thread2;

  public Ticker() {
    Initialize();
  }

  private void Initialize() {
    label1 = new Label();
    label2 = new Label();

    label1.Width = 280;
    label1.Height = 30;
    label1.Location = new Point(1, 10);
    label1.TextAlign = ContentAlignment.MiddleRight;

    label2.Width = 280;
    label2.Height = 30;
    label2.Location = new Point(1, 40);

    this.Controls.Add(label1);
    this.Controls.Add(label2);

    thread1 = new Thread(new ThreadStart(MoveLeft));
    thread1.Start();    

    thread2 = new Thread(new ThreadStart(MoveRight));
    thread2.Start();    
  }

  private void MoveLeft() {
    int counter = 0;
    int max = newsItems.Length;

    while (true) {
      // get news headline
      String headline = newsItems[counter];
      counter++;
      if (counter==max)
        counter = 0;

      for (int i=0; i<=headline.Length; i++) {
        label1.Text = headline.Substring(0, i);
        Thread.Sleep(60);
      }
      Thread.Sleep(100);  
    }    
  }

  private void MoveRight() {
    int counter = 0;
    int max = newsItems.Length;

    while (true) {
      // get news headline
      String headline = businessItems[counter];
      counter++;
      if (counter==max)
        counter = 0;

      for (int i=0; i<=headline.Length; i++) {
        label2.Text = headline.Substring(0, i);
        Thread.Sleep(100);
      }

      Thread.Sleep(100);
    }    
  }


  protected override void OnClosing(CancelEventArgs e) {
    thread1.Join(0);   
    thread2.Join(0);
    Environment.Exit(0);
  }


  public static void Main() {
    Ticker ticker = new Ticker();
    Application.Run(ticker);    
  }
}

First, look at the following part in the Initialize method that is called from the class's constructor.


    thread1 = new Thread(new ThreadStart(MoveLeft));
    thread1.Start();    

    thread2 = new Thread(new ThreadStart(MoveRight));
    thread2.Start();    

Here thread1 and thread2 are instantiated and started. When thread1 is started, it will begin executing the MoveLeft method. When thread2 is started, the MoveRight method will start running.

The MoveLeft method consists of a While loop that runs indefinitely, until the Thread is aborted.


while (true) {
  .
  .
  .
}

The MoveLeft method displays the items in newsItems in turn. When the thread is first started, the MoveLeft method gets the first string into headline because counter is 0.


String headline = newsItems[counter];

Then it increments counter so that the next time the code in the while loop is called the code will get the next string.


counter++;

However, there are only a certain number of strings. So, when the last string in the array is reached, it must go back to the first string.


  if (counter==max)
    counter = 0;

Then, for each string, it does a for loop that goes from zero to the length of the string, constructing a substring starting from an empty string. For each loop, it gets a substring that is one character longer than the substring in the previous loop. As a result, the text on the label looks like running.


      for (int i=0; i<=headline.Length; i++) {
        label1.Text = headline.Substring(0, i);
        Thread.Sleep(60);
    }

For each substring to be readable, it must move slow enough for the reader to read. Therefore, the Sleep method of the Thread class is called and 60 is passed to put the thread to sleep for 60 milliseconds. For each string, it will sleep for 100 milliseconds.


Thread.Sleep(100);

The MoveRight method is very similar to MoveLeft. However, MoveRight works on the business headlines and moves from right to left. Finally, the two threads are terminated when the form is closed. This is achieved by overriding the OnClose method of the Form class. In the OnClose method, the Join method of both thread1 and thread2 is called, causing both threads to be blocked.


    thread1.Join(0);
    thread2.Join(0);

Compiling and Running the Application

To compile the application, follow these steps:

  1. Save the code in Listing 1 into a file called ticker.cs
  2. Open a command prompt and change directory to where the ticker.cs file resides.
  3. Type the following.

csc ticker.cs

A ticker.exe file will be created.

To run the application, type ticker. The application is shown in Figure 1.

Figure 1: The News Ticker application.

Ticker.exe

Copyright © 2009 O'Reilly Media, Inc.