about_Thread_Jobs
Краткое описание
Предоставляет сведения о заданиях на основе потоков PowerShell. Задание потока — это тип фонового задания, выполняющего команду или выражение в отдельном потоке в текущем процессе сеанса.
Подробное описание
PowerShell одновременно выполняет команды и сценарии с помощью заданий. Существует три типа заданий, предоставляемых PowerShell для поддержки параллелизма.
RemoteJob
— команды и скрипты выполняются в удаленном сеансе. Дополнительные сведения см. в about_Remote_Jobs.BackgroundJob
— Команды и скрипты выполняются в отдельном процессе на локальном компьютере. См. дополнительные сведения о заданиях.PSTaskJob
илиThreadJob
— команды и скрипты выполняются в отдельном потоке в одном процессе на локальном компьютере.
Задания на основе потоков не так надежны, как удаленные и фоновые задания, так как они выполняются в одном процессе в разных потоках. Если одно задание имеет критическое сообщение об ошибке, которая завершает процесс, все остальные задания в процессе завершаются.
Однако для заданий на основе потоков требуется меньше затрат. Они не используют уровень удаленного взаимодействия или сериализацию. Результирующий объект возвращается в виде ссылок на динамические объекты в текущем сеансе. Без этой нагрузки задания на основе потоков выполняются быстрее и используют меньше ресурсов, чем другие типы заданий.
Внимание
Родительский сеанс, созданный заданием, также отслеживает состояние задания и собирает данные конвейера. Дочерний процесс задания завершается родительским процессом после того, как задание достигнет готового состояния. Если родительский сеанс завершается, все выполняемые дочерние задания завершаются вместе со своими дочерними процессами.
Существует два способа обойти эту ситуацию:
- Используется
Invoke-Command
для создания заданий, выполняемых в отключенных сеансах. Дополнительные сведения см. в about_Remote_Jobs. - Используется
Start-Process
для создания нового процесса, а не задания. Дополнительные сведения см. в статье Start-Process.
Запуск заданий на основе потоков и управление ими
Существует два способа запуска заданий на основе потоков:
Start-ThreadJob
— из модуля ThreadJobForEach-Object -Parallel -AsJob
— в PowerShell 7.0 добавлена параллельная функция.
Используйте те же командлеты заданий , которые описаны в about_Jobs для управления заданиями на основе потоков.
С использованием Start-ThreadJob
Модуль ThreadJob сначала поставляется с PowerShell 6. Его также можно установить из коллекция PowerShell для Windows PowerShell 5.1.
Чтобы запустить задание потока на локальном компьютере, используйте Start-ThreadJob
командлет с командой или скриптом, заключенным в фигурные скобки ({ }
).
В следующем примере запускается задание потока, которое запускает Get-Process
команду на локальном компьютере.
Start-ThreadJob -ScriptBlock { Get-Process }
Команда Start-ThreadJob
возвращает ThreadJob
объект, представляющий выполняемое задание. Объект задания содержит полезные сведения о задании, включая текущее состояние выполнения. Он собирает результаты задания по мере создания результатов.
С использованием ForEach-Object -Parallel -AsJob
PowerShell 7.0 добавил новый параметр командлета ForEach-Object
. Новые параметры позволяют запускать блоки скриптов в параллельных потоках в качестве заданий PowerShell.
Данные можно передать ForEach-Object -Parallel
в . Данные передаются в блок скрипта, который выполняется параллельно. Параметр -AsJob
создает объекты заданий для каждого из параллельных потоков.
Следующая команда запускает задание, содержащее дочерние задания для каждого входного значения, переданного команде. Каждое дочернее Write-Output
задание выполняет команду с значением входных данных в виде аргумента.
1..5 | ForEach-Object -Parallel { Write-Output $_ } -AsJob
Команда ForEach-Object -Parallel
возвращает PSTaskJob
объект, содержащий дочерние задания для каждого входного значения канала. Объект задания содержит полезные сведения о состоянии выполнения дочерних заданий. Он собирает результаты дочерних заданий по мере создания результатов.
Как ждать завершения задания и получения результатов задания
Можно использовать командлеты заданий PowerShell, такие как Wait-Job
и Receive-Job
ожидать завершения задания, а затем возвращать все результаты, созданные заданием.
Следующая команда запускает задание потока, выполняющее Get-Process
команду, а затем ожидает завершения команды и, наконец, возвращает все результаты данных, созданные командой.
Start-ThreadJob -ScriptBlock { Get-Process } | Wait-Job | Receive-Job
Следующая команда запускает задание, выполняющее Write-Output
команду для каждого входного ввода, а затем ожидает завершения всех дочерних заданий и, наконец, возвращает все результаты данных, созданные дочерними заданиями.
1..5 | ForEach-Object -Parallel { Write-Output $_ } -AsJob | Wait-Job | Receive-Job
Командлет Receive-Job
возвращает результаты дочерних заданий.
1
3
2
4
5
Так как каждое дочернее задание выполняется параллельно, порядок созданных результатов не гарантируется.
Производительность задания потока
Задания потоков являются более быстрыми и легкими, чем другие типы заданий. Но у них по-прежнему есть издержки, которые могут быть большими при сравнении с работой, выполняемой работой.
PowerShell выполняет команды и скрипты в сеансе. В сеансе может выполняться только одна команда или скрипт. Поэтому при выполнении нескольких заданий каждое задание выполняется в отдельном сеансе. Каждый сеанс вносит свой вклад в затраты.
Задания потоков обеспечивают лучшую производительность, если работа, которую они выполняют, больше, чем издержки сеанса, используемого для выполнения задания. Существует два случая, которые соответствуют этим критериям.
Трудоемкая работа — выполнение скрипта в нескольких заданиях потоков может использовать преимущества нескольких ядер процессора и ускорить работу.
Работа состоит из значительного ожидания — скрипт, который тратит время на ожидание результатов ввода-вывода или удаленного вызова. Выполнение параллельно обычно выполняется быстрее, чем при последовательном выполнении.
(Measure-Command {
1..1000 | ForEach { Start-ThreadJob { Write-Output "Hello $using:_" } } | Receive-Job -Wait
}).TotalMilliseconds
36860.8226
(Measure-Command {
1..1000 | ForEach-Object { "Hello: $_" }
}).TotalMilliseconds
7.1975
В первом примере выше показан цикл foreach, который создает 1000 заданий потоков для простой записи строк. Из-за затрат на задание требуется более 36 секунд.
Второй пример запускает ForEach
командлет, чтобы выполнить те же 1000 операций.
На этот раз ForEach-Object
последовательно выполняется в одном потоке без каких-либо затрат на задание. Он завершается всего в 7 миллисекундах.
В следующем примере для 10 отдельных системных журналов собираются до 5000 записей. Так как сценарий включает в себя чтение ряда журналов, то имеет смысл выполнять операции параллельно.
$logNames.count
10
Measure-Command {
$logs = $logNames | ForEach-Object {
Get-WinEvent -LogName $_ -MaxEvents 5000 2>$null
}
}
TotalMilliseconds : 252398.4321 (4 minutes 12 seconds)
$logs.Count
50000
Скрипт завершается в половину времени, когда задания выполняются параллельно.
Measure-Command {
$logs = $logNames | ForEach {
Start-ThreadJob {
Get-WinEvent -LogName $using:_ -MaxEvents 5000 2>$null
} -ThrottleLimit 10
} | Wait-Job | Receive-Job
}
TotalMilliseconds : 115994.3 (1 minute 56 seconds)
$logs.Count
50000
Задания потоков и переменные
Существует несколько способов передачи значений в задания на основе потоков.
Start-ThreadJob
может принимать переменные, которые передаются командлету, передаются в блок скрипта с помощью $using
ключевого слова или передаются через параметр ArgumentList .
$msg = "Hello"
$msg | Start-ThreadJob { $input | Write-Output } | Wait-Job | Receive-Job
Start-ThreadJob { Write-Output $using:msg } | Wait-Job | Receive-Job
Start-ThreadJob { param ([string] $message) Write-Output $message } -ArgumentList @($msg) |
Wait-Job | Receive-Job
ForEach-Object -Parallel
принимает каналы в переменных и переменные, передаваемые непосредственно блоку скрипта с помощью ключевого $using
слова.
$msg = "Hello"
$msg | ForEach-Object -Parallel { Write-Output $_ } -AsJob | Wait-Job | Receive-Job
1..1 | ForEach-Object -Parallel { Write-Output $using:msg } -AsJob | Wait-Job | Receive-Job
Так как задания потоков выполняются в одном процессе, любой ссылочный тип переменной, переданный в задание, необходимо тщательно обрабатывать. Если это не потокобезопасный объект, он никогда не должен быть назначен, а метод и свойства никогда не должны вызываться в нем.
В следующем примере объект .NET ConcurrentDictionary
, безопасный для потока, передается всем дочерним заданиям для сбора объектов процесса с уникальным именем. Так как это потокобезопасный объект, его можно безопасно использовать во время параллельного выполнения заданий в процессе.
$threadSafeDictionary = [System.Collections.Concurrent.ConcurrentDictionary[string,object]]::new()
$jobs = Get-Process | ForEach {
Start-ThreadJob {
$proc = $using:_
$dict = $using:threadSafeDictionary
$dict.TryAdd($proc.ProcessName, $proc)
}
}
$jobs | Wait-Job | Receive-Job
$threadSafeDictionary.Count
96
$threadSafeDictionary["pwsh"]
NPM(K) PM(M) WS(M) CPU(s) Id SI ProcessName
------ ----- ----- ------ -- -- -----------
112 108.25 124.43 69.75 16272 1 pwsh
См. также
PowerShell