Как я могу ускорить цикл foreach PowerShelll - PullRequest
1 голос
/ 31 января 2020

У меня есть сценарий PowerShell, который подключается к базе данных и получает список пользовательских данных. Я беру эти данные и создаю foreach l oop, чтобы запустить скрипт для данных.

Это работает, но медленно, так как результаты могут быть более 1000 записей, и он должен завершить Script.bat для User A, прежде чем он сможет начать User B. Script.bat для одного пользователя не зависит от другого и занимает ~ 30 с для каждого пользователя.

Есть ли способ ускорить это вообще? Я играл с -Parallel, ForEach-Object и workflow, но не могу заставить его работать, скорее всего из-за того, что я нуб в PS.

foreach ($row in $Dataset.tables[0].rows)
{
   $UserID=$row.value
   $DeviceID=$row.value1
   $EmailAddress=$row.email_address

   cmd.exe /c "`"$PSScriptRoot`"\bin\Script.bat -c `" -Switch $UserID`" >> `"$PSScriptRoot`"\${FileName3}_REST_${DateTime}.txt 2> nul";
}

1 Ответ

2 голосов
/ 31 января 2020

Вы сказали это сами, ваше узкое место связано с командным файлом в вашем скрипте, а не с самим l oop. foreach (в отличие от ForEach-Object) уже является более быстрым механизмом foreach l oop в PowerShell. Исследуйте свой пакетный файл , чтобы выяснить, почему для его завершения требуется 30 секунд, и оптимизируйте его, где сможете.


Использование заданий

Примечание: Start-Job запустит задание под другим процессом. Если у вас PowerShell Core , вы можете использовать командлет Start-ThreadJob вместо Start-Job. Это запустит вашу работу как часть другого потока того же процесса вместо запуска другого процесса.

Если вы не можете оптимизировать пакетный скрипт или оптимизировать его под свои нужды, тогда вы можете рассмотреть используя Start-Job, чтобы запустить задание для асинхронного выполнения, а затем проверить результат и получить любые выходные данные, используя Receive-Job. Например:

# Master list of jobs you need to check the result of later
$jobs = New-Object System.Collections.Generic.List[System.Management.Automation.Job]

# Run your script for each row
foreach ($row in $Dataset.tables[0].rows)
{
   $UserID=$row.value
   $DeviceID=$row.value1
   $EmailAddress=$row.email_address

   # Use Start-Job here to kick off the script and store the job information
   # for later retrieval.
   # The $using: scope modifier allows you to make use of variables that were
   # defined in the session calling Start-Job
   $job = Start-Job -ScriptBlock { cmd.exe /c "`"${using:PSScriptRoot}`"\bin\Script.bat -c `" -Switch ${using:UserID}`" >> `"${using:PSScriptRoot}`"\${using:FileName3}_REST_${DateTime}.txt 2> nul"; }

   # Add the execution to the $jobs list to check the result of later
   # Casting to void here prevents the Add method from returning the object
   # we've added.
   [void]$jobs.Add($job)
}

# Wait for the jobs to be done
Write-Host 'Waiting for all jobs to complete...'
while( $jobs | Where-Object { $_.State -eq 'Running' } ){
  Start-Sleep -s 10
}

# Retrieve the output of the jobs
foreach( $j in $jobs ) {
  Receive-Job $j
}

Примечание. Поскольку вам необходимо выполнить этот сценарий ~ 1000 раз, вы можете рассмотреть возможность написания своей логики c, чтобы выполнить только определенное количество заданий в время. В приведенном выше примере запускаются все необходимые задания без указания количества, которое может выполняться одновременно.


Для получения дополнительной информации о заданиях и свойствах, которые вы можете проверить в запущенном / завершенном задании, проверьте ссылки ниже:

* В документации говорится, что область действия using может быть объявлена ​​только при работе с удаленными сеансами, но , похоже, работает нормально с Start-Job, даже если работа местная.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...