Using Wmi Event Registration to Create Thread Thresholds – A Concept
The other day I was contacted by someone through this blog, asking how my eariler thread threshold script could be used in a script where the multithreading was handled by one command.... Tricky....
After pondering it for a bit, I wondered if we could use a WMI event registration to monitor the number of concurrent threads and then at least thrown in a pause for the master script to give the system time to breathe? This is what I came up with...
You call this function once before you start kicking off background tasks. It registers with the WMI event handler using Register-WMIEvent with a query saying "raise an event whenever a powershell process is spawned which has a parent ID of my currend pid". Fortunately $pid is a global variable which contains your current session's PID, so you don't need to go and find it.
From this, you can tag on an action. In my case I have added a pause via start-sleep. My thinking being that you can pause the master thread long enough for your current jobs to complete. Hopefully avoiding a memory issue....
The function has two params, $Threshold which is the max number of concurrent threads you want to run, and $WaitTimeSeconds (you guessed it) which tells the script how long to pause.
Function Set-ThreadThreshold($Threshold,$WaitTimeSeconds) { $BaseThreadCount = (gwmi -query "select threadcount from win32_process where processid = $pid").ThreadCount $TotalThreshold = $Threshold + $BaseThreadCount Register-wmievent –query "Select * From __InstanceCreationEvent Within 1 Where TargetInstance Isa 'Win32_Process' And TargetInstance.Name = 'powershell.exe' and targetinstance.parentprocessid = $pid" –action { if((gwmi -query "select * from win32_process where parentprocessid = $pid" | measure-object).Count -ge $Threshold){ write-host "Pausing Script";Start-Sleep -Seconds $WaitTimeSeconds}} }
This is really a concept idea and is nowhere near a finished product. I just wanted to get the idea out there and see if anyone could suggest a better way of doing it? Let me know
Keeping Track of Your Threads
Since the release of Powershell v2 and the introduction of background tasks, multi threading has become an almost daily programming occurrence for me. If it's something you haven't delved into yet, take a look at the start-job commandlet and it's associated commandlets when you next fireup Powershell on your machine.
Now, multi threading is a fantastic way of getting things done in parallel quicker, when if they were running in serial it would take FOR EVER. For example, one script I changed recently went from taking 8 hours to complete (in serial) to 20 minutes in parallel. Obviously, it worked the server resources harder while it was doing it, but that's what they're there for right? However, before you start on your next multi threaded tool, think how much system resources each instance of your code is going to take up as the biggest problem I encountered is the dreaded OutOfMemory exception, and all of your threads fail. Not good.
So, to get around this, I suggest you pop the following function into any module or script you're going to use background tasks on. It allows to to specify how many concurrent tasks you want to be allowed to run at any one time, and also how long to wait before checking again. It's an iteravite function, so you just need to call it once from within the loop that you're spawning your threads from.
Function Threshold { Param([int]$MaxConcurrent,[int]$PauseTime) $jobs = (get-job -state running | Measure-Object).count $RunningJobs = 0 if($jobs -eq $null){ $RunningJobs = 0 } if($jobs -ne $null){ $RunningJobs = $jobs } If ($RunningJobs -ge $MaxConcurrent){ $TheMessage = "More than " + $MaxConcurrent.tostring() + " jobs running, waiting " + $PauseTime + " seconds" write-output $TheMessage $Completed = (Get-Job -state Completed | Measure-Object).count if($Completed -ne $null){ $TheMessage = "Currently there are " + $completed.tostring() + " completed jobs" write-output $TheMessage } $Running = get-job -state Running write-output "Current Running Jobs:" write-output $running start-sleep -seconds $PauseTime Threshold $MaxConcurrent $PauseTime } }
Just call the above code before you spawn a new thread, and it'll pause your script until there's space for the new threads to spawn. Happy multi threading!



