Displaying Real-Time Stock Information Using Multithreading

ulti threading is one of the most powerful concepts in programming. Using multithreading, you can break a complex task into multiple threads that execute independently of one another. One particularly good application of multithreading is in tasks that are synchronous in nature, such as Web services calls. By default, Web services calls are blocking calls?that is, the caller code will not continue until the Web service returns the result. But because Web services calls are often slow, this could result in sluggish client side performance unless you take special steps to make the call into an asynchronous one.

This article shows how to develop a charting application so you can see how to call Web services asynchronously without freezing the client UI. The sample code uses the Chart FX component to display stock price information using a graph. For readers who would prefer a free charting library written in .NET, refer to the Related Resources section located in the left column of this article.

Creating the Web Service
The sample code needs access to a hypothetical Stock Quote Web service. To build the Web service using Visual Studio .NET 2003, create a new Web service project and name it “StockWS”. This Web service consists of one Web method named getPrice(), which accepts a single stock symbol parameter:

    _   Public Function getPrice(ByVal stock As String) _      As Single      Return Rnd() * 100   End Function

The getPrice() method generates a random price regardless of the stock requested. Its sole purpose is to simulate a real Web service returning the price of a particular stock.

Although this article uses a made-up Web service for demonstration purposes, you can easily substitute a live Web service to display real stock information. Head over to http://www.xmethods.net to see a list of stock Web services.

Displaying Graphs Using the Chart FX Component
After creating the Web service project, add a new Windows application project (name it “Stock Quote”) to the Solution Explorer. Add a Web Reference to the Web service project created earlier. The Solution Explorer should now look like Figure 1.

To build this project, you need to download and install a 30-day trial copy of the Chart FX component from http://chartfx.com/. After installing the Chart component, you should be able to locate it in the Toolbox in Visual Studio .NET 2003 (see Figure 2).

?
Figure 1. The Projects in Solution Explorer: The figure shows the StockWS Web service project and the Windows Forms Stock Quote projects in the Visual Studio Solution Explorer pane.
?
Figure 2. The Chart Component in the Toolbox: You need to download and install a 30-day trial copy of the Chart FX component from http://chartfx.com/ to get the component to appear in the Toolbox.

In the default Form1 of the Windows application, populate the form with the following controls, as shown in Figure 3:

  • Chart
  • ComboBox
  • Button

The Chart Component offers many options for customizing its behavior and look-and-feel. You can format the Chart component using the Wizard (located at the bottom of the Properties window, see Figure 4).

?
Figure 3. The Stock Quote Main Form: The figure shows how the default form should look after populating the form with the appropriate controls.
?
Figure 4. Formatting Wizard for the Chart Component: The Wizard offers numerous options for formatting the Chart Component.

The easiest way to follow the example is to copy and paste the following Chart properties into the “Windows Form Designer Generated” code region of the form in the code-behind class:

   'Chart1   '   Me.Chart1.AxisX.Staggered = True   Me.Chart1.AxisX.Step = 10   Me.Chart1.AxisY.Step = 10   Me.Chart1.BackObject = GradientBackground1   Me.Chart1.DataStyle = _      SoftwareFX.ChartFX.DataStyle.ReadXValues   Me.Chart1.DesignTimeData = _      "C:Program FilesChartFX for .NET " & _      "6.2WizardXYZero.txt"   Me.Chart1.Gallery = SoftwareFX.ChartFX.Gallery.Lines   Me.Chart1.InsideColor = _      System.Drawing.Color.Transparent   Me.Chart1.LineWidth = 3   Me.Chart1.Location = New System.Drawing.Point(40, 16)   Me.Chart1.MarkerShape = _      SoftwareFX.ChartFX.MarkerShape.None   Me.Chart1.Name = "Chart1"   Me.Chart1.NSeries = 1   Me.Chart1.NValues = 20   Me.Chart1.Palette = "HighContrast.HighContrast"   Me.Chart1.PointLabels = True   Me.Chart1.Size = New System.Drawing.Size(656, 216)   Me.Chart1.TabIndex = 12   Me.Chart1.Titles.AddRange(New _      SoftwareFX.ChartFX.TitleDockable() _      {TitleDockable1})

Also, populate the ComboBox control with the following items: “MSFT,” “SUN,” “YHOO,” and “GE.” You can do that in the Form_Load event:

   Me.cmbStocks1.Items.AddRange(New String() {"MSFT", "SUN", "YHOO", "GE"})

Activating the Chart
Next, import the following namespaces (at the top of the code window):

   Imports SoftwareFX.ChartFX   Imports System.Threading

Declare a global variable called t1 to be used for threading:

   Dim t1 As Thread

In the Chart1_Load event, initialize the Chart component:

   Private Sub Chart1_Load(ByVal sender As _      System.Object, ByVal e As System.EventArgs) _      Handles Chart1.Load      '---show the time on the x-axis every 5 point---      Chart1.AxisX.Step = 5         '---use 5 pixels to separate between each point---      Chart1.AxisX.PixPerUnit = 5         '---make chart scrollable---      Chart1.Scrollable = True         '---Open and close the communication channel---      Chart1.OpenData(COD.Values, 1, COD.Unknown)      Chart1.CloseData(COD.Values)   End Sub

Add a new class named StockQuote to the current form. The StockQuote class invokes the Web service and uses the returned stock price to update the chart.

   Public Class StockQuote      '---number of graph to plot in a component      Const NUM_SERIES = 1          Private lastPoint As Integer = 0      Dim stockPrice As Single         Private pStockSymbol As String      Private pStockSeries As Integer = 0      Private pChartControl As Chart         WriteOnly Property StockSymbol()         Set(ByVal Value)            pStockSymbol = Value         End Set      End Property         WriteOnly Property ChartControl()         Set(ByVal Value)            pChartControl = Value         End Set      End Property         Public Sub InvokeWebService()         Dim ws As New StockWS.Service1            For i As Integer = 0 To 10000            stockPrice = ws.getPrice(pStockSymbol)            pChartControl.Invoke(New _               myDelegate(AddressOf updateChart), _               New Object() {})            '---wait for 1 second before continuing            Thread.Sleep(1000)         Next      End Sub         Public Delegate Sub myDelegate()      Public Sub updateChart()         pChartControl.OpenData(COD.Values, NUM_SERIES, _            COD.Unknown)         pChartControl.Value(pStockSeries, lastPoint) = _            stockPrice         '---displays the time on the x-axis         pChartControl.AxisX.Label(lastPoint) = _            DateTime.Now.ToShortTimeString         lastPoint += 1         pChartControl.CloseData(COD.Values)         '---move the scroll bar to the rightmost         pChartControl.AxisX.ScrollPosition = _            pChartControl.AxisX.ScrollSize      End Sub      End Class

You pass the required stock symbol to the StockQuote class via the StockSymbol property, and set the chart to update using the ChartControl property. The InvokeWebService() method calls the Web service repeatedly in a loop (set to 10,000 in this example). As this class will execute in a separate thread, you must take special care to ensure that you do not update a Windows control directly, because Windows controls are not thread-safe. Instead, you use a delegate and call the Invoke() method of the control you want to update. The code calls the Web service every second, as evidenced by the Thread.Sleep(1000) statement.

To start the thread to update the chart with the latest stock information, add code to the Get Stock Quote button’s Click event:

   Private Sub btnGetStockQuote1_Click( _      ByVal sender As System.Object, _      ByVal e As System.EventArgs) _      Handles btnGetStockQuote1.Click         Dim sq As New StockQuote      sq.StockSymbol = cmbStocks1.SelectedItem      sq.ChartControl = Chart1      t1 = New Thread(AddressOf sq.InvokeWebService)      t1.Start()   End Sub
?
Figure 5. Testing the Application: When you select a stock symbol and click the Get Stock Quote button, the results of the repeated calls to the Web service appear on the chart; however, because the Web service runs on a background thread, calling it doesn’t prevent normal UI operations.

The main reason to package the code to invoke the Web service as a class is that the Thread class constructor accepts only a ThreadStart delegate (the delegate that represents the method in which to start the thread), and there’s no overloaded Thread.Start() method that accepts parameter values. Hence the only way to pass parameters into a thread is to package the relevant code to invoke as part of a class. You can then pass parameters in via properties of that class.

To test the code, press F5. Select a stock and click on the Get Stock Quote button. You should now be able to move the window (proving that the UI isn’t locked by the repeated Web service calls) and at the same time see the chart update with the latest stock information (see Figure 5).

Author’s Note: The pricing for the stock selected in Figure 5 is only a simulated one; it is not the actual stock price.

Displaying Multiple Stock Prices
You’ve seen how to call a Web service asynchronously without bogging down the UI of the application; however you can enhance the application by making it display information about more than one stock at a time.

In the same form, add a second set of controls (ChartFX, ComboBox, and a Button) along with a label, Pause, and Stop buttons as shown in Figure 6.

?
Figure 6. Enhanced Multi-Stock Form: The figure shows the new controls you need to add to the default form to chart two stocks simultaneously.

This enhanced example displays two charts simultaneously, and also displays the status of the thread that displays the second chart.

Add a second global variable t2:

   Dim t1, t2 As Thread

The sample project uses a Timer control (located in the Toolbox) to display the status of the second thread. Drag the timer onto the form and set the timer’s Interval property to 500, which causes the timer’s Tick event to fire every half-second (500 milliseconds). Code in the Tick event handler below updates the thread’s status in the label control named lblThreadStatus:

   Private Sub Timer1_Tick( _      ByVal sender As System.Object, _      ByVal e As System.EventArgs) _      Handles Timer1.Tick      lblThreadStatus.Text = "Thread state: " & _         t2.ThreadState.ToString   End Sub   

Use the same chart initialization code for the second chart control as you used for the first:

   Private Sub Chart2_Load( _      ByVal sender As System.Object, _      ByVal e As System.EventArgs) _      Handles Chart2.Load         '---show the time on the x-axis every 5 point---      Chart2.AxisX.Step = 5            '---use 5 pixels to separate between each point---      Chart2.AxisX.PixPerUnit = 5         '---make chart scrollable---      Chart2.Scrollable = True            '---Open and close the communication channel---      Chart2.OpenData(COD.Values, 1, COD.Unknown)      Chart2.CloseData(COD.Values)   End Sub

When you click the Get Stock Quote button for the second chart, the code spins off yet another thread?and also enables the Timer control so the form displays the thread status:

   Private Sub btnGetStockQuote2_Click( _      ByVal sender As System.Object, _      ByVal e As System.EventArgs) _      Handles btnGetStockQuote2.Click         Dim sq As New StockQuote      sq.StockSymbol = cmbStocks2.SelectedItem      sq.ChartControl = Chart2      t2 = New Thread(AddressOf sq.InvokeWebService)      t2.Start()         '---enable the Pause and Stop buttons      btnPauseContinue.Enabled = True      btnStop.Enabled = True      '---Activate the Timer control      Timer1.Enabled = True   End Sub

Press F5 to test the two-chart version (see Figure 7). Select a stock for each chart, and you’ll see the two charts displaying simultaneously.

?
Figure 7. Enhanced Two-Chart Application: The enhanced version displays two charts simultaneously.

When the second thread is running, notice that its status alternates between Running and WaitSleepJoin. This is because a thread is either in execution (Running) or sleeping (WaitSleepJoin). When the thread is paused, its state is WaitSleepJoin, Suspended. When the thread is aborted, its state first changes to AbortRequested and then to Stopped.

To pause the thread, first check the status of the running thread and then use the Suspend() method. After pausing a thread, you can resume it using the Resume() method.

   Private Sub btnPauseContinue_Click( _      ByVal sender As System.Object, _      ByVal e As System.EventArgs) _      Handles btnPauseContinue.Click         ' if thread is sleeping or running then suspend it      If t2.ThreadState = ThreadState.WaitSleepJoin _         Or t2.ThreadState = ThreadState.Running Then         t2.Suspend()         btnPauseContinue.Text = "Continue"      Else         ' resume the thread         t2.Resume()         btnPauseContinue.Text = "Pause"      End If   End Sub

To stop a thread, use the Abort() method:

   Private Sub btnStop_Click( _      ByVal sender As System.Object, _      ByVal e As System.EventArgs) _      Handles btnStop.Click      Try         If Not t2.ThreadState = ThreadState.Stopped Then            btnPauseContinue.Enabled = False            btnStop.Enabled = False            t2.Abort()         End If      Catch ex As Exception         MsgBox(ex.ToString)      End Try   End Sub

As you can see by running the sample project, you can use multithreading to build applications that remain responsive even while performing background tasks. While the example in this article uses Web services, the same principles apply to other types of background tasks. For example, you could adapt this application to read data from external devices such as a thermometer or blood pressure monitoring device.

Share the Post:
Share on facebook
Share on twitter
Share on linkedin

Overview

Recent Articles: