Concurrent Processing (or faux multithreading)

Have you ever needed to run a command against 2000 workstations and thought, "Man that batch file is going to run forever!"  You might even worry the data will be too stale to matter when it finally gets to you.  I’ve run into this situation multiple times and the fix was to write a quick and dirty app in .NET that uses some simple multithreading.  I just change around some strings, recompile, and I’m off to the races.  Not a programmer you say?  Well, you need to learn a little.  And if .NET isn’t your thing, learn some VBScript and PowerShell.  Next year I plan to start abandoning VBScript <sniff> and moving to PowerShell, but because PowerSehll is not standard on all Windows systems yet it is easier to write i VBScript.  So what I provide for you here is a simple VBScript that will run multiple processes concurrently.  This is not multti-threading, so it uses more resources than my .NET code does, but it’s pretty darn cool.
 
So let’s start with the main piece the Class we create to handle our new process.
 
Class RunCmd
  Private p_Shell
  Private p_Exec
  Private p_Name
  Private p_Available
  Private p_ExitCode
  Public PID
  Public EC_Retrieved
 
  Private Sub Class_Initialize()
    Set p_Shell = CreateObject("WScript.Shell")
    p_Exec = Empty
    p_Available = "True"
    EC_Retrieved = ""
  End Sub
 
  Public Sub Open(cmd)
    Set p_Exec = p_Shell.Exec(cmd)
    PID = p_Exec.ProcessID
    p_Available = "False"
    p_ExitCode = ""
    EC_Retrieved = "False"
  End Sub
 
  Public Property Let Name(tempname)
    p_Name = tempname
  End Property
 
  Public Property Get Name
    Name = p_name
  End Property
 
  Public Property Get Available
    If Not IsEmpty(p_Exec) Then
      If p_Exec.Status <> 0 Then
        p_Available = "True"
        p_ExitCode = p_Exec.ExitCode
      End If
    End If
    Available = p_Available
  End Property
 
  Public Property Get ExitCode
    ExitCode = p_ExitCode
    EC_Retrieved = "True"
  End Property
End Class
 
The class has the constructor, one method, and 5 properties.  Next we look at how I used them.
 
First we set up a sample array of computers to test against.  This can be easily read in from Active Directory, a text file, or database table.  Then we create an array to store new instances of our RunCmd class.  In VBScript we cannot use the For Each…Next statement to populate each array element with a new instance of our class, me must use the standard For…Next which requires us to specify the number of items in our array.
 
Computers = Array("comp1","comp2","comp3","comp4","comp5","comp6","comp7")
Dim RunningProcesses(50)
for i = 0 to UBound(RunningProcesses)
  Set RunningProcesses(i) = New RunCmd
Next
 
After the array of RunCmd objetcs is set up it’s time to move to the main loop where the work is started.  StartProcess is called, which verifies the RunCmd object stored in that array element is not currently running an active process and if it is not it starts a new process to connect to the current computer fom the Computers array.  Once the process is started we move to the CheckProcess function which checks the number of objects in the RunningProcesses array have active processes running.  For the example I have set the number to 3.  So once 3 active processes have been reached it will loop until less than 3 are running.  As the CheckProcesses it calls it will retrieve data from the objects that have data.  So far the only thing I am capturing os the ExitCode.  I have not attempted to capture the StdOut as normally VBScript will wait for the process to finish, and we are try to avoid that!
 
For Each computer in computers
  StartProcess(computer)
  Do While CheckProcesses() >= 3
    WScript.Sleep 500
  Loop
Next
 
Once we have looped througn all of the computer we want to connect to we could still have processes in play, so we continue to run CheckProcesses while until we have 0 processes running.
 
Do While CheckProcesses() > 0
Loop
 
Here is the code for the StartProcess subroutine.  We pass the name of the computer we are attempting to connect to then we pass a command to to the Open method of object p.  When the method Open runs it assigns the PID of the process we started to the PID property of the object.
 
Sub StartProcess(cname)
  For Each p in RunningProcesses
    If p.Available = "True" Then
      p.open("cmd /c sc \\" & cname & " query snare|find /i ""_name""")
      p.Name = cname
      WScript.StdOut.WriteLine "Process Started: " & p.pid
      Exit For
    End iF
  Next
End Sub
 
The CheckProcess function is where we determine how many processes are running and grab the exit codes of completed processes.  When we get the Available property it check to see if the p_Exec object inside of our RunCmd object has a status other than 0, which means it has completed.  If it has not completed it increments the ProcessCount variable.  If it has completed it checks the EC_Retrieved property to see if we have already retrieved the ExitCode of the process.  If we have not retrieve it yet access the ExitCode property.  When we request this property it turns the EC_Retrieved property flag to True.  You can then code for whatever the expected error code might be.
 
Function CheckProcesses
  ProcessCount = 0
  For Each p in RunningProcesses
    If p.Available = "False" Then
      ProcessCount = ProcessCount + 1
    Else
      If p.EC_Retrieved = "False" Then
        If p.ExitCode = "1" Then
          WScript.StdOut.WriteLine p.Name & " – Snare is not installed."
        ElseIf p.ExitCode = "0" Then
          WScript.StdOut.WriteLine p.Name & " – Snare is installed."
        End If
      End If
    End If
  Next
  CheckProcesses = ProcessCount
End Function

 

There’s a lot of application for this code, and it’s a fun tutorial (I think) for creating your own classes in VBScript. Making your own class is a powerful tool and it can be used in tons of different applications.  Feel free to post a comment if you have questions or you’d like more examples!

EDIT: I have posted the complete code in the VBScripts download section!
Advertisements

4 responses to “Concurrent Processing (or faux multithreading)

  1. Hi,

    May I ask you to forward to me the source code of “Concurrent Processing (or faux multithreading)” as I couldn’t find it in the article or the download section.

    Appreciate your great help and support.

    Best regards,
    Amr

      • Hello Scott,

        Thank you for this example. I would also like to see the complete source code. Would you be so kind as to please direct me to it’s location on your server or send me a copy as you did for Amr?

        Cheers & Thanks,
        Paul

  2. Thanks Scott but the email blocks any file with “.vbs”. May I ask you to resend it with changing the extension to “.txt” so it can pass through the email filter.

    Best regards,
    Amr

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s