Start-RSJob: Throttle parameter have no effect inside a ForEach-Object block
jnury opened this issue · comments
Hi,
The following code has the expected behavior (2 simultaneous executions):
Measure-Command {
1..10 | Start-RSJob -ScriptBlock { Start-Sleep -Seconds 3 } -Throttle 2 | Wait-RSJob
}
[...]
Seconds : 15
But the following one doesn't (all job run simultaneously):
measure-command {
1..10 | ForEach-Object { Start-RSJob -ScriptBlock { Start-Sleep -Seconds 3 } -Throttle 2 } | Wait-RSJob
}
[...]
Seconds : 3
It's because Start-RSJob is called 1 time in the first code but 10 times in the second.
Do you think it's possible to workaround this behavior ? Maybe with a variable shared between different calls (like WebSession for Invoke-WebRequest) ?
ForEach-Object is really usefull when you want to pass multiple arguments to your scriptblock (or there is another way to do that ?).
hi,
have you tried -argumentlist or -variabletoimport param of start-rsjob ?
Please provide an real example where you want to pass the args.
Thank-you for your answer.
I did not tried VariableToImport so far. This is an example code (totally useless :-D):
$scriptblock = {
param (
$SleepSecond,
$Status
)
Write-Warning "Service was $Status"
Start-Sleep -Seconds $SleepSecond
}
Measure-Command {
Get-Service | Select-Object -First 10 | ForEach-Object {
$status = $_.Status.ToString()
if ($status -eq 'Running') {
$argumentList = @(2, 'Started')
}
else {
$argumentList = @(4, 'Not started')
}
Start-RSJob -ScriptBlock $scriptblock -ArgumentList $argumentList -Name $_.Name -Throttle 2
} | Wait-RSJob | Receive-RSJob
}
The result is 4 seconds (should between 10 and 20).
use -Batch
- it is a key for runspace identification and throttking
Sorry I cannot get your point.
In your given example, whatever the number of service you set in 'select -first', the duration only depends on the max sleep seconds defined in $arguementList. In your case the max is 4, so the result is around 4 seconds ia good for me. start-rsjob is non blocking, the sleep in the start-rsjob scriptblock wont block ForEach.
@MVKozlov : thank you ! It works:
measure-command {
1..10 | ForEach-Object { Start-RSJob -ScriptBlock { Start-Sleep -Seconds 3 } -Throttle 2 -Batch 'Test' } | Wait-RSJob
}
[...]
Seconds : 15
@copdips : the total duration should depends on the average duration of a job AND the Throttle parameter. In my example, it only depends on the max duration of a job.
I think to use really the -Throttle 2 in your example, you may remove the foreach block, and move the part between foreach and start-rsjob to the script block, and you send the service object directly to start-rsjob, than start-rsjob will run in 2 threads at the same time that will take 10 to 20 seconds.
please check.the param $InputObject of start-rsjob, only this param is ValueFromPipeline
, only using it will make sense of -Throttle
. in your example, start-rsjob doesn't use it. be aware that start-rsjob launches only non blocking async runspaces as long as the the maxrunspaces number is not reached in the same runspacepool.
https://github.com/proxb/PoshRSJob/blob/master/PoshRSJob/Public/Start-RSJob.ps1#L170
by using -batch
, all the runspaces will get the same name even they are launched by foreach, so the -throttle defined in the first and the unique runspacepool will be reused each time. without -batch and in a foreach block, every start-rsjob will create a new runspacepool, 10 services so 10 independant runspacepools with only 1 runspace inside, each does its job (sleep) in //.
https://github.com/proxb/PoshRSJob/blob/master/PoshRSJob/Public/Start-RSJob.ps1#L505
https://github.com/proxb/PoshRSJob/blob/master/PoshRSJob/Public/Start-RSJob.ps1#L515
replace -batch "test" by batch "test_$($_.name)" will bring you back to 4 seconds as the runspacepool names are different from each other again.
Thank you for your explanations @copdips
I want to have the same runspacePool for all my jobs as this is the only way to have Throttle working. So everything is OK now with -Batch parameter