Multithreading Powershell能否并行运行命令?
我有一个powershell脚本来对一堆图像进行批处理,我想进行一些并行处理。Powershell似乎有一些后台处理选项,如启动作业、等待作业等,但我发现用于并行工作的唯一好资源是编写脚本文本并运行这些() 理想情况下,我想要类似于.NET4中并行foreach的东西 一些看起来很简单的东西,比如:Multithreading Powershell能否并行运行命令?,multithreading,powershell,parallel-processing,Multithreading,Powershell,Parallel Processing,我有一个powershell脚本来对一堆图像进行批处理,我想进行一些并行处理。Powershell似乎有一些后台处理选项,如启动作业、等待作业等,但我发现用于并行工作的唯一好资源是编写脚本文本并运行这些() 理想情况下,我想要类似于.NET4中并行foreach的东西 一些看起来很简单的东西,比如: foreach-parallel -threads 4 ($file in (Get-ChildItem $dir)) { .. Do Work } 也许我最好直接跳到c#…你可以在Powe
foreach-parallel -threads 4 ($file in (Get-ChildItem $dir))
{
.. Do Work
}
也许我最好直接跳到c#…你可以在PowerShell2中使用执行并行作业。签出和其他作业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
背景作业的设置成本很高,而且不可重复使用。PowerShell MVP Oisin Grehan 具有PowerShell多线程功能 (2010年10月25日网站关闭,但可通过Web存档访问) 我在脚本中使用了一个用于数据加载例程的脚本:
史蒂夫·汤森的回答在理论上是正确的,但在实践中并非如@likwid所指出的那样。我修改后的代码考虑了作业上下文障碍——默认情况下,没有任何内容会跨越该障碍!因此,可以在循环中使用automatic
$\uuu
变量,但不能直接在脚本块中使用,因为它位于作业创建的单独上下文中
要将变量从父上下文传递到子上下文,请使用Start Job
上的-ArgumentList
参数发送变量,并在脚本块内使用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 *
(我通常希望提供对PowerShell文档的引用作为支持证据,但是,唉,我的搜索没有结果。如果您碰巧知道上下文分离是在哪里记录的,请在此处发表评论让我知道!)
我创建了一个invoke async,它允许您同时运行多个脚本块/cmdlet/函数。这对于小型作业(子网扫描或针对100台计算机的wmi查询)非常有用,因为创建运行空间的开销与启动作业的启动时间相比非常大。它可以这样使用
使用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
只是cmdlet/函数
$servers = Get-Content servers.txt
$rtn = Invoke-Async -Set $servers -SetParam computername -Params @{count=1} -Cmdlet Test-Connection -ThreadCount 50
要完成前面的回答,您还可以使用
等待作业
等待所有作业完成:
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
这些天有很多答案:
foreach对象-并行
作为#4的替代方案workflow work {
foreach -parallel ($i in 1..3) {
sleep 5
"$i done"
}
}
work
3 done
1 done
2 done
或具有平行块的工作流:
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
下面是一个带有运行空间的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
在Powershell 7中,可以使用-Parallel
$Message = "Output:"
Get-ChildItem $dir | ForEach-Object -Parallel {
"$using:Message $_"
} -ThrottleLimit 4
如果您正在使用最新的跨平台powershell(顺便说一句),您可以添加单个
&
来运行并行脚本。(使用;
按顺序运行)
在我的例子中,我需要并行运行2个npm脚本:npm run hotReload&npm run dev
您还可以将npm设置为对其脚本使用
powershell
(默认情况下,它在windows上使用cmd
)
从项目根文件夹运行:npm config set script shell pwsh--userconfig./.npmrc
然后使用单个npm脚本命令:npm run start
"start":"npm run hotReload & npm run dev"
这一点已经得到了彻底的回答。只想发布我基于Powershell作业创建的此方法作为参考 作业作为脚本块列表传递。它们可以参数化。 作业的输出是彩色编码的,并以作业索引作为前缀(就像在vs构建过程中一样,因为这将在构建中使用) 可用于一次启动多台服务器或并行运行构建步骤等
function Start-Parallel {
param(
[ScriptBlock[]]
[Parameter(Position = 0)]
$ScriptBlock,
[Object[]]
[Alias("arguments")]
$parameters
)
$jobs = $ScriptBlock | ForEach-Object { Start-Job -ScriptBlock $_ -ArgumentList $parameters }
$colors = "Blue", "Red", "Cyan", "Green", "Magenta"
$colorCount = $colors.Length
try {
while (($jobs | Where-Object { $_.State -ieq "running" } | Measure-Object).Count -gt 0) {
$jobs | ForEach-Object { $i = 1 } {
$fgColor = $colors[($i - 1) % $colorCount]
$out = $_ | Receive-Job
$out = $out -split [System.Environment]::NewLine
$out | ForEach-Object {
Write-Host "$i> "-NoNewline -ForegroundColor $fgColor
Write-Host $_
}
$i++
}
}
} finally {
Write-Host "Stopping Parallel Jobs ..." -NoNewline
$jobs | Stop-Job
$jobs | Remove-Job -Force
Write-Host " done."
}
}
样本输出:
所以我尝试了几次这个建议,但似乎我的变量没有得到正确的扩展。为了使用相同的示例,当这行执行时:
testpath“\\$\uc$\Something”
我希望它将$\uU
扩展到当前项中。然而,事实并非如此。相反,它返回一个空值。这似乎只发生在脚本块内部。如果我在第一个注释后立即写出该值,它似乎工作正常。@likwid-听起来像是站点的一个单独问题。我如何查看在后台运行的作业的输出?@SimpleGuy-请参阅此处以获取有关输出捕获的信息-在后台作业完成之前,您似乎无法可靠地查看它。@SteveTownsend谢谢!实际上,在屏幕上查看输出不是一件很好的事情。来晚了,对我没用。相反,我在新的终端(shell)上启动了一个进程,因此现在每个进程都在不同的终端上运行,这使进度视图变得更好、更清晰。感谢您的回答。我尝试使用您的解决方案,但无法使其完全工作。你能看看我的问题吗:或者,调用一个单独的脚本文件非常容易。只需使用Start Job-FilePath script.ps1-ArgumentList$
另一种方法是执行脚本生成的初步过程,在该过程中除了变量扩展之外什么都不做,然后并行调用生成的脚本。我有一个可以适应脚本生成的小工具,尽管它从来都不支持脚本生成。你可以看到,这很有效。但我无法从ScriptBlock获取实时提要输出流。仅当ScriptBlock返回.tl时才打印输出;dr:receive作业(wait作业($a=start作业{“heyo!”}));删除作业$a
或$a=start job{“heyo!”};等待工作一美元;一份工作可获$1;删除作业$a
另请注意,如果在作业完成之前调用接收作业
,