Advertisement
DaisyDragon

Class.MTTaskList.AKA-MTAnything

Feb 15th, 2025
21
0
Never
1
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PowerShell 23.30 KB | Software | 0 0
  1. <#
  2. #######################################   QUICK COMMAND EXAMPLES   #######################################
  3.  
  4. [MTTaskList]::new($ParameterList, $ScriptBlock, 20).Finalise().Results()
  5.     Creates 20 process threads and runs the script using the list of parameters, waits, closes, returns results.
  6.  
  7. $Work = [MTTaskList]::new($ParameterList, $ScriptBlock, 20)
  8.     Creates 20 process threads and runs the script using the list of parameters. Leaves runnning in the background and
  9.      open to adding more tasks. No variables necessary. By default the number of threads is based on your CPU cores.
  10.      New Parameter(s) can be add with AddTask or AddTaskOverride. A scriptblock here is used as the default to run,
  11.      BUT if the scriptblock is not provided, all new tasks MUST be added with AddTaskOverride.
  12. $Work.AddTask($NewParameters)
  13.     Add new task using the earlier scriptblock and new parameter(s)
  14.     Parameters can be a string like "WSxxxxx020", a hashtable like @{Name="WSxxxxx020"}, or an array/list of those.
  15. $Work.AddTaskOverride($NewParameters, $NewScriptBlock)
  16.     Adds a new task, with a new scriptblock. Typically useful if this TaskList is purely for running many random tasks quickly
  17.     Or for applying modified scriptblocks instead of using parameters.
  18. $Work.Wait().CollectResults()
  19.     Waits for current tasks to finish, collects output for the tasks that are complete and haven't been collected previously.
  20.     Outputs as ThreadTask objects with these properties:
  21.         .Parameters: contain the Parameters of the task
  22.         .Scriptblock: the scriptblock used
  23.         .Result: result output from the scriptblock. only applied when CollectReslts or Finalise are run.
  24.         .Instance: Each tasks has an Instance number starting from 1, to easily find tasks in the order they were assigned.
  25.     Leaves TaskList open to adding more work.
  26. $Work.Close()
  27.     Gets results of completed work so far and closes the TaskList.
  28. $Work.Dispose()
  29.     Force closes the TaskList.
  30. $Work.Finalise()
  31.     Waits for tasks to complete, with a progress bar, then retrieves and stores all results.
  32.     Closes the TaskList so it can't take any new tasks.
  33. $Work.Results()
  34.     Outputs the .Result value of all tasks, whether they've been collected or not. Should only be used when all work
  35.      is complete. Outputs as array of objects, 1 per task, of output from the task's scriptblock.
  36.      Typically that object will be an array of strings, unless your scriptblock outputs another object type.
  37. $Work.Result(1)
  38.     Outputs the .Result value of a specific task based on it's Instance index. (the order the task was added)
  39.     Used for quick reference of work once it's completed and collected.
  40.    
  41. TaskList can bloat resources if not closed properly.
  42. Close, Dispose, or Finalise will properly close the TaskList processes.
  43. Closing your terminal window will also flush any TaskList you may have left running.
  44.  
  45. #############################################   HOW TO USE   #############################################
  46.  
  47. ####################   Main Guide   ####################
  48.  
  49. #############  Including the Class  #############
  50.  
  51. # Copy the Class info below into your console session, or the top of your scriptfile.
  52. # - When using in a script file: MUST be after Params() and before you use it.
  53. # - To declutter your script, you can include this file in the same folder and use the following line.
  54.     . "$PSScriptRoot\Class.MTTaskList.ps1"
  55. # or
  56.     . "Literal:Path\to\File\Class.MTTaskList.ps1"
  57.  
  58.  
  59. #############  Preparing your ScriptBlock and Parameters  #############
  60.  
  61. # Create a ScriptBlock to be reused. Include parameters that you will provide values for later.
  62. # If Parameters are new to you, it's very easy to make a simple set. Parameters are variables set in a "param()" block
  63. #   at the start of a script or scriptblock.
  64. # They can get very advanced, with type validation, positions, sets, Required properties, and more. Research them if you're interested.
  65. # For a basic setup, see below.
  66. $Scriptblock = {     #Scriptblocks are actually [string] objects, basically plain text, but must be wrapped in curly braces: {}
  67. param($var)          #This sets parameter variables that will receive any values to change between runs. Comma separated.
  68. Start-sleep $var     # Do a thing with the given value.
  69. Write-output "Slept for $var seconds"     # This output will be collected later.
  70. }
  71.  
  72. # Generate a list of hashtables that contain parameters for each task.
  73. $worklist = For ($i = 0; $i -lt 100; $i++){
  74. @{var=$i}   # This is a hashtable, with the name on the left being the name of the parameter it will be assigned to in the ScriptBlock
  75.             # Parameter names MUST match. The value to provide for processing is on the right. Multiple entries are separated by semicolon: ;
  76. }
  77.  
  78. # You can now use a simple array of parameters.
  79. #   For example: a Ping script, and the WorkList is an array of targets. @("Computer1","Computer2")
  80. # An array of Arrays is NOT SUPPORTED at this time.
  81.  
  82. # TEST your script and hashtable design by running it like below. Use only 1 hashtable of parameters to test.
  83. # This method is called "Splatting" and is the basis of easily applying data to a reuseable script.
  84. $hashParameters = @{varA="fu";varB="bar"}
  85. .$Scriptblock @hashParameters
  86.  
  87.  
  88. #############  Starting work  #############
  89.  
  90. # The TaskList goes through 5 steps: Initialisation, Adding Work, Complete Work, Collect Results, Close TaskList.
  91. # Here we will Initialise and Add Work.
  92. # Use the [MTTaskList] class to create a tasklist that will process your tasks.
  93. $workload = [MTTaskList]::new($Scriptblock, 5)
  94. # Provide the Scriptblock and the MaxThreads value (Maximum amount of processes you want to run at a time).
  95.  
  96. $workload = [MTTaskList]::new($worklist, $Scriptblock, 5)
  97. # You can provide the list of task variables and they will be added as tasks when creating the list.
  98. # You can leave out the MaxThreads and it will use the number of processors instead.
  99.  
  100. # You can add more tasks later as long as the list hasn't been closed. The original scriptblock is the default script used.
  101. $workload.AddTask($worklist)
  102.  
  103. # You can also add tasks with a new scriptblock override. This is not encouraged, but allows for a general processing pool.
  104. $workload.AddTaskOverride($worklist, $ScriptBlock)
  105.  
  106. # If working in a console session, you can track progress by running this:
  107. $workload.Progress()
  108.  
  109.  
  110. #############  Retrieving Results  #############
  111.  
  112. # This is the second half of the process, but a big half. How to retrieve and handle the output of your scriptblock.
  113. # How you handle it is up to you, but let's start with retrieving it.
  114.  
  115. $workload.Tasks
  116. # Returns all Task objects. These objects contain 4 useful properties:
  117. #  "Instance"   : Unique task ID, in the order it was added, starting at 0. Usable reference when correlating results with submission order.
  118. #  "Parameters" : The hashtable or collection of parameters originally supplied.
  119. #  "Results"    : The output of the script. This is empty until work is complete and ".CollectResults()" or ".Wait()" are run.
  120. #  "Scriptblock": Hidden value - The script used. Useful when checking what was run, especially when AddTaskOverride is used.
  121.  
  122. $workload.IsCompleted()
  123. # Returns true/false if all tasks have completed running.
  124.  
  125. $workload.IsCollected()
  126. # Returns true/false if all tasks have collected output into their Result property.
  127. # IMPORTANT! Result Collection is not automatic. It is triggered during ".Finalise()" or by ".CollectResults()".
  128.  
  129. $workload.CollectResults()
  130. # Collects results for any completed tasks that haven't already been collected, copying the Output of each task to it's
  131. #  respective .Tasks.Result property. Each task can only be collected once.
  132. # This will output Task objects from "$workload.Tasks" but filtered for just the current collection round.
  133. # It is best to capture this in a variable or pipe it straight into your output handler.
  134.  
  135. $workload.Wait()
  136. # Halts the terminal and waits for all supplied tasks to complete. Shows progress bar.
  137. # IMPORTANT! When running this in script, please note: THIS CANNOT BE CANCELLED
  138.  
  139. $workload.ResultByIndex($Number)
  140. # This returns the Result property of the Task at the provided Instance number.
  141. # This is an alternative to "$workload.Tasks[$Number].Result", this should be more convenient and easier to understand.
  142. # NOTE: Instances start at "0" to match the Array[Row] format so you can ForEach through your own array and easily reference the same object.
  143. # NOTE: Result value is an empty placeholder until .CollectResults(), .Close(), or .Finalise() is run.
  144.  
  145. $workload.Results()
  146. # Is similar to "$workload.Tasks.Result", returning the Result property of all tasks.
  147. # BUT each array item is a completely separate result, and can be processed per item.
  148. # $workload.Tasks.Result will merge all results into one array. This will keep them separate items.
  149. # Mainly used to conveniently get output after .Finalise() is run on terminal session tasks.
  150. # In all other cases, you should access results by:
  151. #   $workload.Tasks[$Number].Result
  152. #   $workload.ResultByIndex($Number)
  153. #   $workload.CollectResults() | ForEach-Object {$_.Result}
  154. # NOTE: Result value is an empty placeholder until .CollectResults(), .Close(), or .Finalise() is run.
  155.  
  156.  
  157. # To handle output data as it is completed, make a loop similar to the one below.
  158. # This is good for logging progress or saving changes in case of script interruption.
  159. # When Multithreading, there should still be a management process that handles all tasks, and that is your script.
  160. While ($workload.IsCollected() -eq $false) {
  161.     $workload.CollectResults() |  Foreach-object {
  162.         #Do something with $_.Result
  163.         #Check $_.Parameters.varA for value, etc.
  164.     }
  165.     start-sleep 1
  166. }
  167.  
  168.  
  169. #############  Cleanup  #############
  170.  
  171. # Always close the TaskList when you are finished with it. This doesn't erase the list, just closes the background processing of tasks.
  172. # No new tasks can be added. Do this when you are definitely not going to add any more work.
  173.  
  174. $workload.Close()
  175. $ Collects results for tasks currently completed, and closes the TaskList.
  176. # This will collect processed results before closing the list as a matter of cleanup.
  177. # It WILL NOT wait for the tasks to finish and will forcibly close the list.
  178.  
  179. $workload.Dispose()
  180. # Force closes the processing pool without waiting or collecting results.
  181.  
  182. $workload.Finalise()
  183. # This will wait until all work is complete, copies the task results, and closes the TaskList.
  184. # This is good for simple, or single phase scripts where you want to use multithreading for better performance,
  185. #  or for terminal use to quickly run a batch of tasks and wait for the output.
  186. # IMPORTANT! When running this in script, please note: THIS CANNOT BE CANCELLED
  187.  
  188.  
  189. ####################   Basic Example 1   ####################
  190.  
  191. # Include the Class block
  192. Class MTTaskList {}
  193.  
  194. # Creat ScriptBlock
  195. $Scriptblock = {...}
  196.  
  197. # Setup the required Task pool
  198. $workload = [MTTaskList]::new($Scriptblock, 6)
  199.  
  200. # Add the work. This will involve collecting data and organising into Parameter Sets for the script
  201. . blah blah blah get data
  202. $worklist = $AListOfComputerOrOtherStuff | ForEach-Object {
  203.     @{Parameter1="Hello";Computer="$_";Parameter3="World"}
  204. }
  205. $workload.AddTask($worklist)
  206.  
  207. # Create a main loop that will handle post-processing of work.
  208. While ($workload.IsCollected() -eq $false) {
  209.     $workload.CollectResults() |  Foreach-object {
  210.         # Do Some stuff
  211.         $_.Result | Where {# Something you need}
  212.         # This is the place where you decide what to do with what comes out of the scriptblock.
  213.         # Was it successful? Do you need to remove it from a ToDo list?
  214.     }
  215.    
  216.     # It's good to wait a bit before looping to process more work, or this loop will use 100% of the CPU.
  217.     start-sleep 1
  218. }
  219.  
  220. # Close the TaskList when you're finished with it
  221. $workload.Close()
  222.  
  223. # Do any other end-of-script work you may need
  224. ........
  225.  
  226.  
  227.  
  228. ####################   Advanced example 2   ####################
  229.  
  230. # You can setup as many Task pools as you want. But keep in mind they all share the same computer resources.
  231. # The main reason I do this, and one of the big benefits to a multiphase script, is when you separate the task into
  232. #   different Scriptblocks that have different loads that you can plan for.
  233. # I have a network file copy SB where 10 threads is about the best performance before bottlenecking the network interface,
  234. #   and then remotely executing that file is just waiting for the other end to finish. Due to install time and other factors,
  235. #   it is efficient ro run the install commands separately instead of waiting for a filecopy thread to be free.
  236. #   And since that can take a long time, but little processing, I give it more threads.
  237.  
  238. # Include the Class block
  239. Class MTTaskList {}
  240.  
  241. # Creat ScriptBlocks
  242. $SB1 = {...}
  243. $SB2 = {...}
  244.  
  245. # Setup the required Task pools
  246. $workload1 = [MTTaskList]::new($SB1, 6)
  247. $workload2 = [MTTaskList]::new($SB2, 12)
  248.  
  249. # Add the work. This will involve collecting data and organising into Parameter Sets for the scripts
  250. . blah blah blah get data
  251. $worklist = $AListOfComputerOrOtherStuff | ForEach-Object {
  252.     @{Parameter1="Hello";Computer="$_";Parameter3="World"}
  253. }
  254. $workload1.AddTask($worklist)
  255.  
  256.  
  257. # Create a main loop that will handle task migration from the first phase to the second phase, and
  258. #   also if you want to log notes or save progress, etc, you'll want that post-processing to happen here.
  259. While (($workload1.IsCollected() -eq $false) -or ($workload2.IsCollected() -eq $false)) {
  260.     $workload1.CollectResults() |  Foreach-object {
  261.         # Do Some stuff
  262.         # Find returned values that the next phase depends on.
  263.         $_.Result | Where {# Something you need}
  264.         # Construct the hashtable to pass to phase 2
  265.         $NewParameters = $_.Parameters
  266.         $NewParameters.NewParameter = # whatever
  267.         # If, for whatever reason, you want to carry the whole output, you can do like this, just make sure SB2 has a parameter for it:
  268.         $NewParameters.SB1Output = $_.Result
  269.         # Add as a new task to the Phase 2 task pool
  270.         $workload2.AddTask($NewParameters)
  271.     }
  272.     $workload2.CollectResults() |  Foreach-object {
  273.         # Do Some stuff
  274.         $_.Result | Where {# Something you need}
  275.         # This is the place where you decide what to do with what comes out of the second scriptblock.
  276.         # Was it successful? Do you need to remove it from a ToDo list?
  277.     }
  278.    
  279.     # It's good to wait a bit before looping to process more work, or this loop will run 100% of the CPU.
  280.     start-sleep 1
  281. }
  282.  
  283.  
  284. # Close the TaskLists when you're finished with them
  285. $workload1.Close()
  286. $workload2.Close()
  287.  
  288. # Do any other end-of-script work you may need
  289. ........
  290.  
  291. #>
  292.  
  293.  
  294.  
  295.  
  296.  
  297.  
  298.  
  299.  
  300. Class MTTaskList {
  301.     [System.Collections.Generic.List[psobject]]                 $Tasks
  302.     [string]                                                    $ScriptBlock
  303.     [int32]                                                     $MaxThreads
  304.     hidden [System.Management.Automation.Runspaces.RunspacePool]$RunspacePool
  305.    
  306.     # Constructors ---------------------------------------------------------------------
  307.     MTTaskList() {
  308.         $this.MaxThreads = $env:number_of_processors
  309.         $this.Init()
  310.     }
  311.    
  312.     MTTaskList([int32]$MaxThreads) {
  313.         $this.MaxThreads = $MaxThreads
  314.         $this.Init()
  315.     }
  316.    
  317.     MTTaskList([string]$ScriptBlock) {
  318.         $this.MaxThreads = $env:number_of_processors
  319.         $this.ScriptBlock = $ScriptBlock
  320.         $this.Init()
  321.     }
  322.    
  323.     MTTaskList([string]$ScriptBlock, [int32]$MaxThreads) {
  324.         $this.MaxThreads = $MaxThreads
  325.         $this.ScriptBlock = $ScriptBlock
  326.         $this.Init()
  327.     }
  328.    
  329.     MTTaskList([hashtable[]]$ParameterSets, [string]$ScriptBlock) {
  330.         $this.MaxThreads = $env:number_of_processors
  331.         $this.ScriptBlock = $ScriptBlock
  332.         $this.Init()
  333.         $this.AddTask($ParameterSets)
  334.     }
  335.    
  336.     MTTaskList([hashtable[]]$ParameterSets, [string]$ScriptBlock, [int32]$MaxThreads) {
  337.         $this.MaxThreads = $MaxThreads
  338.         $this.ScriptBlock = $ScriptBlock
  339.         $this.Init()
  340.         $this.AddTask($ParameterSets)
  341.     }
  342.    
  343.     MTTaskList([string[]]$ParameterSets, [string]$ScriptBlock) {
  344.         $this.MaxThreads = $env:number_of_processors
  345.         $this.ScriptBlock = $ScriptBlock
  346.         $this.Init()
  347.         $this.AddTask($ParameterSets)
  348.     }
  349.    
  350.     MTTaskList([string[]]$ParameterSets, [string]$ScriptBlock, [int32]$MaxThreads) {
  351.         $this.MaxThreads = $MaxThreads
  352.         $this.ScriptBlock = $ScriptBlock
  353.         $this.Init()
  354.         $this.AddTask($ParameterSets)
  355.     }
  356.    
  357.     # Methods --------------------------------------------------------------------------
  358.     # *** Shared Initialiser Method ***
  359.     hidden Init() {
  360.         # Initialiser: creates a RunspacePool to be used and creates empty List for Tasks
  361.         # Generating the list later causes problems.
  362.         $this.RunspacePool = [RunspaceFactory]::CreateRunspacePool(1, $this.MaxThreads)
  363.         $this.RunspacePool.Open()
  364.         $this.Tasks = [System.Collections.Generic.List[psobject]]::new()
  365.     }
  366.    
  367.     # Method for adding new ThreadTask objects with the default scriptblock
  368.     [MTTaskList] AddTask($ParameterSets) {
  369.         If ($this.ScriptBlock) {
  370.             $this.AddTaskOverride($ParameterSets, $this.ScriptBlock)
  371.         } Else {Throw "No default scriptblock, and none provided"}
  372.         return $this
  373.     }
  374.    
  375.     # Method for adding a new Task with a specific ScriptBlock
  376.     [MTTaskList] AddTaskOverride([string]$ScriptBlock) {
  377.         $this.AddTaskOverride(@{}, $ScriptBlock)
  378.         return $this
  379.     }
  380.    
  381.     # Method for adding a new Task with a ScriptBlock override, and String parameters
  382.     [MTTaskList] AddTaskOverride([System.Collections.Generic.List[string]]$ParameterSets, [string]$ScriptBlock) {
  383.         ForEach ($parameterSet in $ParameterSets) {
  384.             # Creates a nested scriptblock so that plain string parameters can be applied with "-ArgumentList"
  385.             $actioning = $this.AddTaskOverride(@{}, "Invoke-Command -Scriptblock {$($ScriptBlock)} -ArgumentList $parameterSet")
  386.             # Modifies the Parameters record
  387.             $actioning.Tasks[-1].Parameters = $parameterSet
  388.         }
  389.         return $this
  390.     }
  391.    
  392.     # AddTask method with a ScriptBlock Override, used as the primary Adding method, all others are Aliases
  393.     [MTTaskList] AddTaskOverride([System.Collections.Hashtable[]]$ParameterSets, [string]$ScriptBlock) {
  394.         If ($this.RunspacePool.IsDisposed -eq $false) {
  395.             ForEach ($parameterSet in $ParameterSets) {
  396.                 $instanceNumber = If ($this.Tasks){$this.Tasks.Count}Else{0}
  397.                 ###  This section generates an object like a class without requiring a separate class  ###
  398.                 # First run only: Adds 'ThreadTask' Class info to TypeData for current session/scope only
  399.                 # This way a separate class isn't needed. Specifically needed to hide some properties.
  400.                 If (-not (get-typedata ThreadTask)) {
  401.                     $typeData = @{
  402.                         TypeName = 'ThreadTask'
  403.                         DefaultDisplayPropertySet = 'Instance','Parameters','Result'
  404.                     }
  405.                     Update-TypeData @TypeData
  406.                 }
  407.                 $newTask = [PSCustomObject]@{
  408.                     PSTypeName = 'ThreadTask'
  409.                     Instance = $instanceNumber
  410.                     Parameters = $parameterSet
  411.                     ScriptBlock = $ScriptBlock
  412.                     Result = @()
  413.                     Shell = [PowerShell]::Create().AddScript($Scriptblock).AddParameters($parameterSet)
  414.                 }
  415.                 $newTask.Shell.RunspacePool = $this.RunspacePool
  416.                 $newTask | Add-Member -MemberType NoteProperty -Name Status -Value $newTask.Shell.BeginInvoke()
  417.                 ###  End of Object creation  ###
  418.                 $this.Tasks.Add($newTask)
  419.             }
  420.         } Else {throw "This TaskList has been closed and cannot accept any more tasks."}
  421.         return $this
  422.     }
  423.    
  424.     # Alias Method for collecting results. Used as primary collector method.
  425.     [array] CollectResults() {
  426.         If (-not $this.Iscollected()) {
  427.             If (-not $this.IsCompleted()) {
  428.                 return $this.CollectNewResults()
  429.             } Else {
  430.                 return $this.CollectAllResults()
  431.             }
  432.         } Else {return @()}
  433.     }
  434.    
  435.     [array] CollectNewResults() {
  436.         If ($this.RunspacePool.IsDisposed -eq $false) {
  437.             Try {
  438.                 $completedTasks = $this.Tasks | Where-Object { $_.Status.IsCompleted -eq $true }
  439.                 foreach ($completedTask in $completedTasks) {
  440.                     $completedTask.Result = $completedTask.Shell.EndInvoke($completedTask.Status)
  441.                     $completedTask.Status = $null
  442.                     $completedTask.Shell.Dispose()
  443.                 }
  444.                 return $completedTasks
  445.             }
  446.             Catch {$this.Dispose();throw "An error was received while retrieving your results. Your MTTaskList has been closed.`nPlease review your parameters and scriptblock, and try again.`n$($error[0])"}
  447.         } Else {throw "This TaskList has been closed. Cannot collect results."}
  448.     }
  449.    
  450.     [array] CollectAllResults() {
  451.         $totalCount = ($this.Tasks).count
  452.         # Collection loop, needed because some work gets stuck in completed and can take a while to collect.
  453.         $collectedResults = @()
  454.         while (-not $this.IsCollected()) { #Loading bar to show collection progress
  455.             $collectedCount = ($this.Tasks | Where-Object Status -eq $null).count
  456.             [int]$taskProgress = $collectedCount / $totalCount * 100
  457.             Write-Progress -Activity "Collecting results for all Tasks" -Status "$taskProgress% Complete:" -PercentComplete $taskProgress
  458.             $collectedResults += $this.CollectNewResults()
  459.             Start-Sleep -Milliseconds 25
  460.         }
  461.         return $collectedResults
  462.     }
  463.    
  464.     # Method to Wait until all new results are complete.
  465.     [MTTaskList] Wait() {
  466.         $totalCount = ($this.Tasks).count
  467.         $finishedCount = 0
  468.         while (-not $this.IsCompleted()) { #Loading bar to show processing progress
  469.             $finishedCount = $totalCount - ($this.Tasks.Status | Where-Object IsCompleted -eq $false).count
  470.             [int]$taskProgress = $finishedCount / $totalCount * 100
  471.             Write-Progress -Activity "Waiting for all Tasks to complete" -Status "$taskProgress% Complete:" -PercentComplete $taskProgress
  472.             Start-Sleep -Milliseconds 25
  473.         }
  474.         return $this
  475.     }
  476.    
  477.     # Method to collect results and close the Runspace Pool
  478.     [void] Close() {
  479.         [void] $this.CollectResults()
  480.         $this.Dispose()
  481.     }
  482.    
  483.     # Method to forcibly close the Runspace Pool
  484.     [void] Dispose() {
  485.         $this.RunspacePool.Close()
  486.         $this.RunspacePool.Dispose()
  487.         $this.Tasks | ForEach-Object {$_.Shell.Dispose()}
  488.     }
  489.    
  490.     # Aggregate Method to wait until all work is complete, collect all results, and dispose the processing pool.
  491.     [MTTaskList] Finalise() {
  492.         [void] $this.Wait()
  493.         [void] $this.CollectAllResults()
  494.         $this.Dispose()
  495.         return $this
  496.     }
  497.    
  498.     # Method to report if all work is completed or not.
  499.     [bool] IsCompleted() {
  500.         If ($this.Tasks.Status.IsCompleted -eq $false) {return $false} Else {return $true}
  501.     }
  502.    
  503.     # Method to report if all work is collected or not. '.IsCompleted' is a sub-property that is removed when work is collected.
  504.     [bool] IsCollected() {
  505.         If ($this.Tasks.Status -ne $null) {return $false} Else {return $true}
  506.     }
  507.    
  508.     # Method to give a progress report
  509.     [array] Progress() {
  510.         $totalCount = ($this.Tasks).count
  511.         If ($totalCount -gt 0) {
  512.             $unfinishedCount = ($this.Tasks.Status | Where-Object IsCompleted -eq $false).count
  513.             $queuedCount = $unfinishedCount - $this.MaxThreads
  514.             $collectedCount = ($this.Tasks | Where-Object Status -eq $null).count
  515.             $output = @()
  516.             $output += "Queued:    " + $(If ($queuedCount -gt 0) {$queuedCount} Else {"0"})
  517.             $output += "Running:   " + $(If ($unfinishedCount -gt $this.MaxThreads) {$this.MaxThreads} Else {$unfinishedCount})
  518.             $output += "Completed: " + ($totalCount - $unfinishedCount)
  519.             $output += "Progress:  " + (($totalCount - $unfinishedCount)/$totalCount).tostring("P")
  520.             $output += "Collected: " + $collectedCount
  521.             return $output
  522.         } Else {
  523.             return "No work has been assigned yet"
  524.         }
  525.     }
  526.    
  527.     # Collection method to return all results
  528.     [array] Results() {
  529.         $collatedResults = [System.Collections.Generic.List[psobject]]::new()
  530.         $this.Tasks | ForEach-Object {$collatedResults.Add($_.Result)}
  531.         return $collatedResults
  532.     }
  533.    
  534.     # Collection method to return session Result based on Instance Index
  535.     [array] Result([int32]$Number) {
  536.         return ($this.Tasks | Where-Object Instance -eq $Number).Result
  537.     }
  538. }
Advertisement
Comments
  • DaisyDragon
    116 days
    # text 0.50 KB | 0 0
    1. The top section is an extensive manual explaining how to use it in scripts or just in the prompt. There may be bugs, I'm always improving it.
    2.  
    3. It does have a .Wait() mode with a progress bar, or you can leave it running in the background and call .Progress() to get a simple progress report. As long as it hasn't been closed, finalised, or disposed, you can add new tasks to it. It's primarily intended for many jobs of the same type, but has an .AddTaskOverride() so you can use it as a general processing pool.
Add Comment
Please, Sign In to add comment
Advertisement