Как выполнить функцию Powershell несколько раз параллельно
Я не уверен, нужно ли это требовать многопоточности, на основе работы или async, но в основном у меня есть функция Powershell script, которая принимает несколько параметров, и мне нужно называть ее несколько раз с разными параметры и выполните их параллельно.
В настоящее время я вызываю функцию следующим образом:
Execute "param1" "param2" "param3" "param4"
Как я могу вызвать это несколько раз, не дожидаясь, когда каждый вызов будет выполнен, чтобы выполнить возврат к вызывающему абоненту?
В настоящее время я запускаю v2.0, но при необходимости могу обновить
EDIT: вот что у меня до сих пор, что не работает:
$cmd = {
param($vmxFilePath,$machineName,$username,$password,$scriptTpath,$scriptFile,$uacDismissScript,$snapshotName)
Execute $vmxFilePath $machineName $username $password $scriptTpath $scriptFile $uacDismissScript $snapshotName
}
Start-Job -ScriptBlock $cmd -ArgumentList $vmxFilePath, $machineName, $username $password, $scriptTpath, $scriptFile, $uacDismissScript, $snapshotName
Я получаю сообщение об ошибке:
cannot convert 'system.object[]' to the type 'system.management.automation.scriptblock' required by parameter 'initializationscript'. specified method is not supported
EDIT2: я изменил свой script, но я все еще получаю ошибку, упомянутую выше. Здесь мой мод:
$cmd = {
param($vmxFilePath,$machineName,$username,$password,$scriptTpath,$scriptFile,$uacDismissScript,$snapshotName)
Execute $vmxFilePath $machineName $username $password $scriptTpath $scriptFile $uacDismissScript $snapshotName
}
Start-Job -ScriptBlock $cmd -ArgumentList $vmxFilePath, $machineName, $username $password, $scriptTpath, $scriptFile, $uacDismissScript, $snapshotName
Ответы
Ответ 1
Для этого не требуется обновление. Определите блок script и используйте Start-Job
для запуска блока script столько раз, сколько необходимо. Пример:
$cmd = {
param($a, $b)
Write-Host $a $b
}
$foo = "foo"
1..5 | ForEach-Object {
Start-Job -ScriptBlock $cmd -ArgumentList $_, $foo
}
Блок script принимает 2 параметра $a
и $b
, которые передаются параметром -ArgumentList
. В приведенном выше примере задания $_
→ $a
и $foo
→ $b
. $foo
является просто примером настраиваемого, но статического параметра.
Запустите Get-Job | Remove-Job
в какой-то момент, чтобы удалить готовые задания из очереди (или Get-Job | % { Receive-Job $_.Id; Remove-Job $_.Id }
, если вы хотите получить результат).
Ответ 2
Вот быстрый фиктивный скриптовый блок для тестирования:
$Code = {
param ($init)
$start = Get-Date
(1..30) | % { Start-Sleep -Seconds 1; $init +=1 }
$stop = Get-Date
Write-Output "Counted from $($init - 30) until $init in $($stop - $start)."
}
Этот скриптовый блок затем может быть передан на Start-Job
, например, 3 параметра (10, 15, 35)
$jobs = @()
(10,15,35) | % { $jobs += Start-Job -ArgumentList $_ -ScriptBlock $Code }
Wait-Job -Job $jobs | Out-Null
Receive-Job -Job $jobs
Это создает 3 задания, назначает их переменной $jobs
, запускает их параллельно и затем ждет завершения этих трех заданий и извлекает результаты:
Counted from 10 until 40 in 00:00:30.0147167.
Counted from 15 until 45 in 00:00:30.0057163.
Counted from 35 until 65 in 00:00:30.0067163.
Это не заняло 90 секунд, только 30.
Одна из сложнейших частей состоит в предоставлении -Argumentlist
до Start-Job
и включает блок param()
внутри ScriptBlock. В противном случае ваши значения никогда не будут видны скриптблоком.
Ответ 3
Мне жаль, что все пропустили вашу проблему - я знаю, что уже слишком поздно, но...
Эта ошибка вызвана тем, что в вашем списке отсутствует запятая между $username и $password.
Вы можете проверить это с помощью этого фрагмента, который я смоделировал из предыдущих ответов:
$cmd = {
param($a, $b, $c, $d)
}
$foo = "foo"
$bar = "bar"
start-job -scriptblock $cmd -ArgumentList "a", $foo, $bar, "gold" #added missing comma for this to work
Ответ 4
Вы можете использовать альтернативу, которая может быть быстрее, чем вызов заданий, если функция не длинная. Макс. Поток - 25, и я только вызываю эту функцию 10 раз, поэтому я ожидаю, что моя общая продолжительность работы составит 5 секунд. Вы можете обернуть Measure-Command вокруг оператора 'results =', чтобы просмотреть статистику.
Пример:
$ScriptBlock = {
Param ( [int]$RunNumber )
Start-Sleep -Seconds 5
Return $RunNumber
}
$runNumbers = @(1..10)
$MaxThreads = 25
$runspacePool = [RunspaceFactory ]::CreateRunspacePool(1, $MaxThreads)
$runspacePool.Open()
$pipeLines = foreach($num in $runNumbers){
$pipeline = [powershell]::Create()
$pipeline.RunspacePool = $runspacePool
$pipeline.AddScript($ScriptBlock) | Out-Null
$pipeline.AddArgument($num) | Out-Null
$pipeline | Add-Member -MemberType NoteProperty -Name 'AsyncResult' -Value $pipeline.BeginInvoke() -PassThru
}
#obtain results as they come.
$results = foreach($pipeline in $pipeLines){
$pipeline.EndInvoke($pipeline.AsyncResult )
}
#cleanup code.
$pipeLines | % { $_.Dispose()}
$pipeLines = $null
if ( $runspacePool ) { $runspacePool.Close()}
#your results
$results