0

#my script is a excerpt of https://www.codeproject.com/Tips/895840/Multi-Threaded-PowerShell-Cookbook #the first example #the issue is where ".AddScript($secb)" is. All jobs are finished sequentially , Could anyone explain ??? #why in my script , .AddScript($sb) is concurrent ??



$numThreads = 5
# Create session state
$myString = "this is session state!"
$sessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
$sessionstate.Variables.Add((New-Object -TypeName System.Management.Automation.Runspaces.SessionStateVariableEntry -ArgumentList "myString" ,$myString, "example string"))
   
# Create runspace pool consisting of $numThreads runspaces
$RunspacePool = [RunspaceFactory]::CreateRunspacePool(1, 5, $sessionState, $Host)
$RunspacePool.Open()

$Jobs = @()
$sb={
param ($data)
$r=Get-Random
Write-Host "before $r"
Start-Sleep -Seconds 3
Write-Host "after $r"
}
$secb={
param ($block)
Invoke-Command -ScriptBlock $block
}
1..5 | % {
    #below line is not concurrent  , i don't know why
    $Job = [powershell]::Create().AddScript($secb) # $Job = [powershell]::Create().AddScript($sb) could do multi-thread
    $Job.AddArgument($sb)

    $Job.RunspacePool = $RunspacePool
    $Jobs += New-Object PSObject -Property @{
      RunNum = $_
      Job = $Job
      Result = $Job.BeginInvoke()
   }
}
 
Write-Host "Waiting.." -NoNewline
Do {
   Write-Host "." -NoNewline
   Start-Sleep -Seconds 1
} While ( $Jobs.Result.IsCompleted -contains $false) #Jobs.Result is a collection

H Marcus
  • 21
  • 4
  • If you can install modules I would definitely recommend the [ThreadJob Module](https://learn.microsoft.com/en-us/powershell/module/threadjob/start-threadjob?view=powershell-7.1). It's just as fast as RunSpaces and a lot easier use. – Santiago Squarzon Apr 29 '21 at 14:01
  • Or foreach-object -parallel in powershell 7. – js2010 Apr 29 '21 at 14:11
  • @js2010 Actually I would love to see how well performs `Foreach-Object -Parallel` vs `ThreadJob` and `RunSpace`. I have one for `ThreadJob` vs `RunSpace` vs `Linear Loop`. – Santiago Squarzon Apr 29 '21 at 14:22
  • Thank you guys , you're so kind!! The reason I use Runspace is because the environments are out of control , the only thing I can assure is they are all PS5.1 – H Marcus Apr 29 '21 at 23:01

1 Answers1

1

I'm by far not an expert on RunSpaces, usually whenever I need multithreading I use the ThreadJob module. I'm not sure what you are doing wrong in your code but I can show you a working example for what you're trying to do. I took this example from this answer (credits to Mathias) which is excellent and modified it a bit.

Code:

cls
$elapsedTime = [System.Diagnostics.Stopwatch]::StartNew()

$numThreads = 5
# Create session state
$myString = "this is session state!"
$sessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
$sessionstate.Variables.Add((New-Object -TypeName System.Management.Automation.Runspaces.SessionStateVariableEntry -ArgumentList "myString" ,$myString, "example string"))
   
# Create runspace pool consisting of $numThreads runspaces
$RunspacePool = [RunspaceFactory]::CreateRunspacePool(1, 5, $sessionState, $Host)
$RunspacePool.Open()

$runspaces = foreach($i in 1..5)
{
    $PSInstance = [powershell]::Create().AddScript({
        param ($TestNumber)

        Write-Output "Test Number: $TestNumber"

        $r={Get-Random}

        Write-Output "before $(& $r)"
        Start-Sleep -Seconds 3
        Write-Output "after $(& $r)"

    }).AddParameter('TestNumber',$i)

    $PSInstance.RunspacePool = $RunspacePool

    [pscustomobject]@{
        Instance = $PSInstance
        Result = $PSInstance.BeginInvoke()
    }
}
 
while($runspaces|Where-Object{-not $_.Result.IsCompleted})
{
    Start-Sleep -Milliseconds 500
}

$resultRunspace = [collections.generic.list[string]]::new()

$Runspaces|ForEach-Object {
    $resultRunspace.Add($_.Instance.EndInvoke($_.Result))
}

$elapsedTime.Stop()
"Elapsed Time: {0}" -f $elapsedTime.Elapsed.TotalSeconds
""
$resultRunspace

Result:

Elapsed Time: 3.1271587

Test Number: 1 before 474010429 after 2055432874
Test Number: 2 before 1639634857 after 1049683678
Test Number: 3 before 72786850 after 2046654322
Test Number: 4 before 1958738687 after 1832326064
Test Number: 5 before 1944958392 after 1652518661

Now, again, if you are able to install modules I would recommend ThreadJob as it is a lot easier to use and performs equally as fast as RunSpaces.

I wrote a script some time ago which would loop through all the directories in $HOME and get the number of files on each one, the script was meant to compare Linear Loops vs ThreadJob vs RunSpace here are the results and why I would always recommend ThreadJob

enter image description here

Santiago Squarzon
  • 41,465
  • 5
  • 14
  • 37
  • Thanks Santiago , I'd like to try , however , I have to run the script on others' machine , which is out of control . Whereas I have to employ runspace – H Marcus Apr 30 '21 at 00:29
  • @HMarcus Well you have an example of working Runspaces here you just need to change what's inside the `.AddScript({ })` block and make sure it returns your desired output (this is why I changed `Write-Host` to `Write-Output` from your example. – Santiago Squarzon Apr 30 '21 at 02:22