Threading - it's always a pain in the rear when you need to run it, but damn if the results aren't amazing when it gets going!

With the new 4.0 framework threading and parallelism has skyrocketed into a new height and become even easier for developers to access.

In this tutorial I'll be explaining the new "Parallel.For" loop. At it's heart, each iteration of the loop spawns a thread for some process to run on. Take one long sequential series of operations and break them into chunks so they can run at the same time!

Required:
4.0 framework

Threading Background:
When your application runs it creates a thread on your computer to execute. A dedicated process line that exists while your application is running. This is usually fine but sometimes you can lock up your application as some intensive task is processing. Usually the symptom is your application "hangs" or goes into a "not responding" state.

To avoid that from happening you can create new threads that branch off from your application, off load the process on their own time, and it won't gum up your main branch. No more hanging!

Additionally you can do things like crunch series of numbers - at the same time - and get the results faster. It's pretty nifty.

Think of it as branches coming off a tree trunk. When the thread processing is done the thread closes. There's a host of problems that can occur when threading (problems talking to each other, finishing at different times, etc), so it is best to read further before you decide to implement threading.

This tutorial:
In this tutorial I'll show you two examples of parallel.for in action. The first is a simple race between threads. If you run it a few times you can see the threads do not all start or end at the same time.

The second tutorial is a simplified version of a problem I had to tackle recently. I had a very large record set to process so I created a small class to access, process, and save records between a given set of IDs. This cut the application time down considerably.

In this instance I am just adding numbers between ranges. The idea is if you find the ranges up front, feed that to your class, you should be able to cut down the processing time.

The parallel.for has a few ways of running, but the simplest is the way illustrated here.

Parallel.For(start_value, ending_value, delegate routine)

Much like how a regular for loop goes from a starting point to an ending point that concept is the same. What differs is instead of sequentially working (and waiting) through the body of the for loop - the parallel.for creates a thread to process the body (aka the delegate routine) in parallel! As fast as the parallel.for loop can create threads for each iteration is as fast as the parallel threads can start!

Required imports

Imports System.Threading
Imports System.Threading.Tasks

I created a small 'dummy class' that houses my two processing functions. This just highlights how you can encapsulate your logic in objects in a clean and efficient manner.

Dummy class

Spoiler

Public Class Dummy
Private _sName As String = String.Empty '-- Class ID.
Public Sub New(ByVal val As String) '-- Constructor
_sName = val
Console.WriteLine(String.Format("{0} created!", val))
End Sub
'-- A simple sprint to the end of a regular for loop. This should high light that each instance of the parallel.for loop is running in it's own thread.
Public Sub Process()
Console.WriteLine(String.Format("{0} : starting!", _sName)) '-- marker
For a As Int32 = 0 To 50
Console.WriteLine(String.Format("{0}: {1}", _sName, a)) '-- print the object's name and the value in the for loop
Next
Console.WriteLine(String.Format("{0} : DONE!", _sName)) '--marker for being done.
End Sub
'-- add up the values between two intervals.
Public Function Process2(ByVal myStart As Int32, ByVal myStop As Int32) As Int64
Dim lReturn As Int64 = 0
'-- comment out the console line to make it run faster
Console.WriteLine(String.Format("{0}: adding values from {1} to {2}", _sName, myStart, myStop))
For i As Int32 = myStart To myStop
lReturn += i
Next
'-- comment out the console line to make it run faster
Console.WriteLine(String.Format("{0}: ending value {1}", _sName, lReturn))
Return lReturn
End Function
End Class

Example 1

'-- ----------- Example 1 -----------
'-- A holder for the five names that will signify our five threads.
Dim sNames As String() = {"A", "B", "C", "D", "E"}
'-- Invoke the name, the starting position, the ending position, and a delegate sub that is your parallel.for's "body".
'-- i is the counter for 0 to sname.
Parallel.For(0, sNames.Count, Sub(i)
Dim temp As New Dummy(sNames(i)) '-- Create an instance of our dummy class with a string name.
temp.Process() '-- start the processing.
End Sub)

Example 2

'-- ----------- Example 2 -----------
'-- In this example I want the sum of each value between 1 and 1000000. I'll break this problem down into intervals, feed them to a class to process, and run it in parallel!
'-- This very well could be used for crunching similar chunks of data, scanning blocks of IP addresses, or calculating the next generation in a genetic algorithm.
Dim myT As New Stopwatch() '-- start a timer to see how fast this takes.
Dim lTotal As Int64 = 0 '-- our output bucket
Dim lReturedVal As Int64 = 0
Dim lStart As Int32 = 1 '-- the value to start counting in.
Dim lInterval As Int32 = 1000000 '-- intervals
Dim lIntervalHolder As New List(Of Int32) '-- Up front work to get our descrete intervals.
Console.WriteLine("Create our intervals")
For i As Int32 = 1 To sNames.Count '-- avoid any over lap!
lIntervalHolder.Add(lStart)
lStart += lInterval + 1
lIntervalHolder.Add(lStart - 1)
Next
Console.WriteLine("Display our intervals")
For i As Int32 = 0 To lIntervalHolder.Count - 1
Console.WriteLine(lIntervalHolder(i))
Next
Console.WriteLine("--------------------------------")
Console.WriteLine("Parallel count")
myT.Start()
Parallel.For(0, sNames.Count, Sub(i)
Dim temp As New Dummy(sNames(i)) '-- Create an instance of our dummy class with a string name.
lReturedVal = temp.Process2(lIntervalHolder(i * 2), lIntervalHolder(i * 2 + 1)) '-- start the processing.
lTotal += lReturedVal '-- add the returned total to the general total.
End Sub)
myT.Stop()
Console.WriteLine(String.Format("Parallel total: {0}", lTotal))
Console.WriteLine(myT.Elapsed)
Console.WriteLine("--------------------------------")
Dim lRegular As Int64 = 0
Console.WriteLine("regular count")
myT = Stopwatch.StartNew
For i As Int32 = lIntervalHolder(0) To lIntervalHolder(lIntervalHolder.Count - 1)
lRegular += i
Next
myT.Stop()
Console.WriteLine(String.Format("regular total: {0}", lRegular))
Console.WriteLine(myT.Elapsed)

Imports System.Threading
Imports System.Threading.Tasks
Public Class Dummy
Private _sName As String = String.Empty '-- Class ID.
Public Sub New(ByVal val As String) '-- Constructor
_sName = val
Console.WriteLine(String.Format("{0} created!", val))
End Sub
'-- A simple sprint to the end of a regular for loop. This should high light that each instance of the parallel.for loop is running in it's own thread.
Public Sub Process()
Console.WriteLine(String.Format("{0} : starting!", _sName)) '-- marker
For a As Int32 = 0 To 50
Console.WriteLine(String.Format("{0}: {1}", _sName, a)) '-- print the object's name and the value in the for loop
Next
Console.WriteLine(String.Format("{0} : DONE!", _sName)) '--marker for being done.
End Sub
'-- add up the values between two intervals.
Public Function Process2(ByVal myStart As Int32, ByVal myStop As Int32) As Int64
Dim lReturn As Int64 = 0
'-- comment out the console line to make it run faster
Console.WriteLine(String.Format("{0}: adding values from {1} to {2}", _sName, myStart, myStop))
For i As Int32 = myStart To myStop
lReturn += i
Next
'-- comment out the console line to make it run faster
Console.WriteLine(String.Format("{0}: ending value {1}", _sName, lReturn))
Return lReturn
End Function
End Class
Module Module1
Sub Main()
'-- ----------- Example 1 -----------
'-- A holder for the five names that will signify our five threads.
Dim sNames As String() = {"A", "B", "C", "D", "E"}
'-- Invoke the name, the starting position, the ending position, and a delegate sub that is your parallel.for's "body".
'-- i is the counter for 0 to sname.
Parallel.For(0, sNames.Count, Sub(i)
Dim temp As New Dummy(sNames(i)) '-- Create an instance of our dummy class with a string name.
temp.Process() '-- start the processing.
End Sub)
'-- ----------- Example 2 -----------
'-- In this example I want the sum of each value between 1 and 1000000. I'll break this problem down into intervals, feed them to a class to process, and run it in parallel!
'-- This very well could be used for crunching similar chunks of data, scanning blocks of IP addresses, or calculating the next generation in a genetic algorithm.
Dim myT As New Stopwatch() '-- start a timer to see how fast this takes.
Dim lTotal As Int64 = 0 '-- our output bucket
Dim lReturedVal As Int64 = 0
Dim lStart As Int32 = 1 '-- the value to start counting in.
Dim lInterval As Int32 = 1000000 '-- intervals
Dim lIntervalHolder As New List(Of Int32) '-- Up front work to get our descrete intervals.
Console.WriteLine("Create our intervals")
For i As Int32 = 1 To sNames.Count '-- avoid any over lap!
lIntervalHolder.Add(lStart)
lStart += lInterval + 1
lIntervalHolder.Add(lStart - 1)
Next
Console.WriteLine("Display our intervals")
For i As Int32 = 0 To lIntervalHolder.Count - 1
Console.WriteLine(lIntervalHolder(i))
Next
Console.WriteLine("--------------------------------")
Console.WriteLine("Parallel count")
myT.Start()
Parallel.For(0, sNames.Count, Sub(i)
Dim temp As New Dummy(sNames(i)) '-- Create an instance of our dummy class with a string name.
lReturedVal = temp.Process2(lIntervalHolder(i * 2), lIntervalHolder(i * 2 + 1)) '-- start the processing.
lTotal += lReturedVal '-- add the returned total to the general total.
End Sub)
myT.Stop()
Console.WriteLine(String.Format("Parallel total: {0}", lTotal))
Console.WriteLine(myT.Elapsed)
Console.WriteLine("--------------------------------")
Dim lRegular As Int64 = 0
Console.WriteLine("regular count")
myT = Stopwatch.StartNew
For i As Int32 = lIntervalHolder(0) To lIntervalHolder(lIntervalHolder.Count - 1)
lRegular += i
Next
myT.Stop()
Console.WriteLine(String.Format("regular total: {0}", lRegular))
Console.WriteLine(myT.Elapsed)
Stop
End Sub
End Module

Advanced topics to explore:
Implement this with data access and record processing
Try breaking out a long sequential math calculation!
Explore having your threads "talk" to each other.