Как выполнить функцию Powershell несколько раз параллельно - программирование
Подтвердить что ты не робот

Как выполнить функцию 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
4b9b3361

Ответ 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