multithreading Powershell 可以并行运行命令吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4016451/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Can Powershell Run Commands in Parallel?
提问by Alan Hymanson
I have a powershell script to do some batch processing on a bunch of images and I'd like to do some parallel processing. Powershell seems to have some background processing options such as start-job, wait-job, etc, but the only good resource I found for doing parallel work was writing the text of a script out and running those (PowerShell Multithreading)
我有一个 powershell 脚本可以对一堆图像进行批处理,我想做一些并行处理。Powershell 似乎有一些后台处理选项,例如启动作业、等待作业等,但我发现用于并行工作的唯一好资源是编写脚本文本并运行它们(PowerShell 多线程)
Ideally, I'd like something akin to parallel foreach in .net 4.
理想情况下,我想要类似于 .net 4 中的并行 foreach 的东西。
Something pretty seemless like:
一些非常相似的东西:
foreach-parallel -threads 4 ($file in (Get-ChildItem $dir))
{
.. Do Work
}
Maybe I'd be better off just dropping down to c#...
也许我最好还是直接使用 c#...
采纳答案by Steve Townsend
You can execute parallel jobs in Powershell 2 using Background Jobs. Check out Start-Joband the other job cmdlets.
您可以使用后台作业在 Powershell 2 中执行并行作业。查看Start-Job和其他作业 cmdlet。
# Loop through the server list
Get-Content "ServerList.txt" | %{
# Define what each job does
$ScriptBlock = {
param($pipelinePassIn)
Test-Path "\$pipelinePassIn\c`$\Something"
Start-Sleep 60
}
# Execute the jobs in parallel
Start-Job $ScriptBlock -ArgumentList $_
}
Get-Job
# Wait for it all to complete
While (Get-Job -State "Running")
{
Start-Sleep 10
}
# Getting the information back from the jobs
Get-Job | Receive-Job
回答by Michael Sorens
The answer from Steve Townsend is correct in theory but not in practice as @likwid pointed out. My revised code takes into account the job-context barrier--nothing crosses that barrier by default! The automatic $_
variable can thus be used in the loop but cannot be used directly within the script block because it is inside a separate context created by the job.
正如@likwid 指出的那样,Steve Townsend 的答案在理论上是正确的,但在实践中却并非如此。我修改后的代码考虑了工作上下文障碍——默认情况下没有任何东西跨越这个障碍!因此,自动$_
变量可以在循环中使用,但不能直接在脚本块中使用,因为它位于作业创建的单独上下文中。
To pass variables from the parent context to the child context, use the -ArgumentList
parameter on Start-Job
to send it and use param
inside the script block to receive it.
要将变量从父上下文传递到子上下文,请使用-ArgumentList
on 参数Start-Job
发送它并param
在脚本块内使用它来接收它。
cls
# Send in two root directory names, one that exists and one that does not.
# Should then get a "True" and a "False" result out the end.
"temp", "foo" | %{
$ScriptBlock = {
# accept the loop variable across the job-context barrier
param($name)
# Show the loop variable has made it through!
Write-Host "[processing '$name' inside the job]"
# Execute a command
Test-Path "$name"
# Just wait for a bit...
Start-Sleep 5
}
# Show the loop variable here is correct
Write-Host "processing $_..."
# pass the loop variable across the job-context barrier
Start-Job $ScriptBlock -ArgumentList $_
}
# Wait for all to complete
While (Get-Job -State "Running") { Start-Sleep 2 }
# Display output from all jobs
Get-Job | Receive-Job
# Cleanup
Remove-Job *
(I generally like to provide a reference to the PowerShell documentation as supporting evidence but, alas, my search has been fruitless. If you happen to know where context separation is documented, post a comment here to let me know!)
(我通常喜欢提供 PowerShell 文档的参考作为支持证据,但是,唉,我的搜索没有结果。如果您碰巧知道记录上下文分离的位置,请在此处发表评论让我知道!)
回答by jrich523
http://gallery.technet.microsoft.com/scriptcenter/Invoke-Async-Allows-you-to-83b0c9f0
http://gallery.technet.microsoft.com/scriptcenter/Invoke-Async-Allows-you-to-83b0c9f0
i created an invoke-async which allows you do run multiple script blocks/cmdlets/functions at the same time. this is great for small jobs (subnet scan or wmi query against 100's of machines) because the overhead for creating a runspace vs the startup time of start-job is pretty drastic. It can be used like so.
我创建了一个 invoke-async,它允许您同时运行多个脚本块/cmdlet/函数。这对于小型作业(子网扫描或针对 100 台机器的 wmi 查询)非常有用,因为创建运行空间与启动作业的启动时间的开销非常大。它可以像这样使用。
with scriptblock,
使用脚本块,
$sb = [scriptblock] {param($system) gwmi win32_operatingsystem -ComputerName $system | select csname,caption}
$servers = Get-Content servers.txt
$rtn = Invoke-Async -Set $server -SetParam system -ScriptBlock $sb
just cmdlet/function
只是 cmdlet/函数
$servers = Get-Content servers.txt
$rtn = Invoke-Async -Set $servers -SetParam computername -Params @{count=1} -Cmdlet Test-Connection -ThreadCount 50
回答by js2010
There's so many answers to this these days:
这几天有很多答案:
- jobs (or threadjobs in PS 6/7 or the module)
- start-process
- workflows
- powershell api with another runspace
- invoke-command with multiple computers, which can all be localhost (have to be admin)
- multiple session (runspace) tabs in the ISE, or remote powershell ISE tabs
- Powershell 7 has a
foreach-object -parallel
as an alternative for #4
- 作业(或 PS 6/7 或模块中的线程作业)
- 启动进程
- 工作流程
- 带有另一个运行空间的 powershell api
- 多台计算机的调用命令,都可以是本地主机(必须是管理员)
- ISE 中的多个会话(运行空间)选项卡,或远程 powershell ISE 选项卡
- Powershell 7 有一个
foreach-object -parallel
替代#4
Here's workflows with literally a foreach -parallel:
这里的工作流程实际上是一个 foreach -parallel:
workflow work {
foreach -parallel ($i in 1..3) {
sleep 5
"$i done"
}
}
work
3 done
1 done
2 done
Or a workflow with a parallel block:
或具有并行块的工作流:
function sleepfor($time) { sleep $time; "sleepfor $time done"}
workflow work {
parallel {
sleepfor 3
sleepfor 2
sleepfor 1
}
'hi'
}
work
sleepfor 1 done
sleepfor 2 done
sleepfor 3 done
hi
Here's an api with runspaces example:
这是一个带有运行空间示例的 api:
$a = [PowerShell]::Create().AddScript{sleep 5;'a done'}
$b = [PowerShell]::Create().AddScript{sleep 5;'b done'}
$c = [PowerShell]::Create().AddScript{sleep 5;'c done'}
$r1,$r2,$r3 = ($a,$b,$c).begininvoke() # run in background
$a.EndInvoke($r1); $b.EndInvoke($r2); $c.EndInvoke($r3) # wait
($a,$b,$c).streams.error # check for errors
($a,$b,$c).dispose() # clean
a done
b done
c done
回答by Chad Miller
Backgrounds jobs are expensive to setup and are not reusable. PowerShell MVP Oisin Grehan has a good exampleof PowerShell multi-threading.
后台作业设置成本高且不可重用。PowerShell MVP Oisin Grehan 有一个很好的 PowerShell 多线程示例。
(10/25/2010 site is down, but accessible via the Web Archive).
(10/25/2010 站点已关闭,但可通过 Web Archive 访问)。
I'e used adapted Oisin script for use in a data loading routine here:
我在这里使用了改编的 Oisin 脚本以用于数据加载例程:
http://rsdd.codeplex.com/SourceControl/changeset/view/a6cd657ea2be#Invoke-RSDDThreaded.ps1
http://rsdd.codeplex.com/SourceControl/changeset/view/a6cd657ea2be#Invoke-RSDDThreaded.ps1
回答by Thomas
To complete previous answers, you can also use Wait-Job
to wait for all jobs to complete:
要完成之前的答案,您还可以使用Wait-Job
等待所有作业完成:
For ($i=1; $i -le 3; $i++) {
$ScriptBlock = {
Param (
[string] [Parameter(Mandatory=$true)] $increment
)
Write-Host $increment
}
Start-Job $ScriptBlock -ArgumentList $i
}
Get-Job | Wait-Job | Receive-Job
回答by izharsa
In Powershell 7 you can use ForEach-Object-Parallel
在 Powershell 7 中,您可以使用ForEach-Object-Parallel
$Message = "Output:"
Get-ChildItem $dir | ForEach-Object -Parallel {
"$using:Message $_"
} -ThrottleLimit 4