Search Forum
(57242 Postings)
Search Site/Articles

Archived Articles
712 Articles

C# Books
C# Consultants
What Is C#?
Download Compiler
Code Archive
Archived Articles
Advertise
Contribute
C# Jobs
Beginners Tutorial
C# Contractors
C# Consulting
Links
C# Manual
Contact Us
Legal

GoDiagram for .NET from Northwoods Software www.nwoods.com


              
Printable Version

Multi Threaded Calculations
By Kara Hewett

Introduction

This C# .NET test harness configures easily comparing throughput results of multi-threaded and single threaded calculations using ADO .NET datasets. This simple multi-threaded example performs calculation and database updates three times faster than a single threaded approach.

Creating the Workspace

To start the test harness, open a Microsoft Visual Studio 2005, .NET Framework Version 2.0 development environment and create two Console Applications. The first application performs calculations using a scheduler and worker threads. The second application performs calculations in a single thread.

Code

Both applications create an ADO .NET dataset of inventory from a fictitious database table called INVENTORY containing fields for the ID, COST, PRICE and PROFIT. The sample code includes database SQL scripts containing the ID and COST for sample sizes up to 10000 data points. The test harness calculates the PRICE and PROFIT for each data point and updates the database.

The multi-threaded test harness starts a worker thread for each calculation and database update.

public class CalculatorScheduler
{
    private DataSet m_invDataSet;
    
    public void Init()
    {
        try
        {
           m_invDataSet = (new InventoryData()).GetDataSet();
           foreach (DataRow row in m_invDataSet.Tables[Properties.Settings.Default.TABLE_DS].Rows)
           {
               Thread t = new Thread(new ParameterizedThreadStart(ProcessCalculations));
               t.Start(row);
           }
        }
        catch (Exception exc)
        {
           exc.Message.ToString();
        }
   }
   private void ProcessCalculations(object row)
   {
        new CalculatorWorker().Calc((DataRow) row);
   }
}


public class CalculatorWorker
{
     private static Decimal MARGIN = Convert.ToDecimal(1.1);
     private static object item = new object();

     public void Calc(DataRow row)
     {
         try
         {
          row.BeginEdit();
          row[Properties.Settings.Default.PRICE] = Convert.ToDecimal(row[Properties.Settings.Default.COST]) * MARGIN;
          row[Properties.Settings.Default.PROFIT] = Convert.ToDecimal(row[Properties.Settings.Default.PRICE]) - Convert.ToDecimal(row[Properties.Settings.Default.COST]);
          row.EndEdit();
          row.AcceptChanges();
          UpdateInventory(row);
         }
        catch (Exception exc)
        {
          exc.Message.ToString();
        }
   }

  private void UpdateInventory(DataRow row)
  {
      try
      {
        OdbcCommand m_UpdateRatingsCmd = new OdbcCommand(Properties.Settings.Default.UPDATE, DBWrapper.Instance.InitializeConnection());
        m_UpdateRatingsCmd.Parameters.AddWithValue("@PRICE", Convert.ToDouble(row[Properties.Settings.Default.PRICE]));
        m_UpdateRatingsCmd.Parameters.AddWithValue("@PROFIT", Convert.ToDouble(row[Properties.Settings.Default.PROFIT]));
        m_UpdateRatingsCmd.Parameters.AddWithValue("@ID", Convert.ToInt32(row[Properties.Settings.Default.ID]));
        m_UpdateRatingsCmd.ExecuteNonQuery();
        DBWrapper.Instance.CloseConnection();
     }
     catch (Exception exc)
     {
        exc.Message.ToString();
     }
}
}
Whereas the single threaded test harness performs the calculations linearly.
m_invDataSet = (new InventoryData()).GetDataSet();
foreach (DataRow row in m_invDataSet.Tables[Properties.Settings.Default.TABLE_DS].Rows)
{
     try
    {
         row.BeginEdit();
         row[Properties.Settings.Default.PRICE] = Convert.ToDecimal(row[Properties.Settings.Default.COST]) * MARGIN;
         row[Properties.Settings.Default.PROFIT] = Convert.ToDecimal(row[Properties.Settings.Default.PRICE]) - Convert.ToDecimal(row[Properties.Settings.Default.COST]);
         row.EndEdit();
         row.AcceptChanges();
       
         OdbcCommand m_UpdateRatingsCmd = new OdbcCommand(Properties.Settings.Default.UPDATE, DBWrapper.Instance.InitializeConnection());
         m_UpdateRatingsCmd.Parameters.AddWithValue("@PRICE", Convert.ToDouble(row[Properties.Settings.Default.PRICE]));
         m_UpdateRatingsCmd.Parameters.AddWithValue("@PROFIT", Convert.ToDouble(row[Properties.Settings.Default.PROFIT]));
         m_UpdateRatingsCmd.Parameters.AddWithValue("@ID", Convert.ToInt32(row[Properties.Settings.Default.ID]));
         m_UpdateRatingsCmd.ExecuteNonQuery();
         DBWrapper.Instance.CloseConnection();
    }
}
 
Tests of different sample sizes show significantly faster results from the multi-threaded solution compared to the single threaded solution. All Start Time and End Time values are in seconds. In this simple example the multi-threaded test harness achieves an average of 1000 calculation and database updates per second for a dataset of 5000 records more than three times the rate of the single threaded approach. The test harnesses could be modified to perform more complex calculations for larger datasets; however, this simple example presents the benefits of multi-threaded calculations.

Multi-Threaded Test Harness for Data Set of 1000 333 calculation and db updates per second

Start Time        : 27 
End Time          : 30 

Single Threaded Test Harness For Data Set of 1000
200 calculation and db updates per second

Start Time        : 23
End Time          : 32

Multi-Threaded Test Harness for Data Set of 5000
1000 calculation and db updates per second

Start Time        : 10
End Time          : 15

Single Threaded Test Harness for Data Set of 5000
333 calculation and db updates per second

Start Time        : 19
End Time          : 34
       
Download Source & SQL