Powershell可以并行运行命令吗?

时间:2010-10-25 16:00:39

标签: multithreading powershell parallel-processing

我有一个powershell脚本对一堆图像进行一些批处理,我想做一些并行处理。 Powershell似乎有一些后台处理选项,如启动作业,等待作业等,但我找到的并行工作的唯一好资源是编写脚本文本并运行它们(PowerShell Multithreading)< / p>

理想情况下,我喜欢类似于.net 4中的并行foreach。

有点像:

foreach-parallel -threads 4 ($file in (Get-ChildItem $dir))
{
   .. Do Work
}

也许我最好只是下降到c#...

9 个答案:

答案 0 :(得分:88)

史蒂夫汤森的回答在理论上是正确的,但在实践中却没有,正如@likwid指出的那样。我修改后的代码考虑了 作业上下文障碍 - 默认情况下没有任何内容超过这个障碍!因此,自动$_变量可以在循环中使用,但不能直接在脚本块中使用,因为它位于作业创建的单独上下文中。

要将变量从父上下文传递到子上下文,请使用-ArgumentList上的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 *

(我通常喜欢提供PowerShell文档的参考作为支持证据,但是,唉,我的搜索没有结果。如果您碰巧知道上下文分离的位置,请在此发表评论以告诉我!)< / p>

答案 1 :(得分:82)

您可以使用Background Jobs在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

答案 2 :(得分:8)

http://gallery.technet.microsoft.com/scriptcenter/Invoke-Async-Allows-you-to-83b0c9f0

我创建了一个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 / function

$servers = Get-Content servers.txt 

$rtn = Invoke-Async -Set $servers -SetParam computername -Params @{count=1} -Cmdlet Test-Connection -ThreadCount 50

答案 3 :(得分:7)

后台作业设置昂贵且无法重复使用。 PowerShell MVP Oisin Grehan 有一个很好的PowerShell多线程示例http://www.nivot.org/2009/01/22/CTP3TheRunspaceFactoryAndPowerShellAccelerators.aspx

(10/25/2010网站已关闭):

我在这里使用了改编的Oisin脚本用于数据加载例程:

http://rsdd.codeplex.com/SourceControl/changeset/view/a6cd657ea2be#Invoke-RSDDThreaded.ps1

答案 4 :(得分:2)

要完成先前的答案,您还可以使用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

答案 5 :(得分:1)

在Powershell 7中,您可以使用ForEach-Object -Parallel

$Message = "Output:"
Get-ChildItem $dir | ForEach-Object -Parallel {
    "$using:Message $_"
} -ThrottleLimit 4

答案 6 :(得分:1)

如果您正在使用最新的跨平台Powershell(应该顺便说一下)https://github.com/powershell/powershell#get-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"

答案 7 :(得分:1)

此问题已得到彻底解答。只想发布我基于Powershell-Jobs创建的这种方法作为参考。

作业作为脚本块列表传递。可以对其进行参数化。 作业的输出用颜色编码并以作业索引为前缀(就像在vs-build-process中一样,因为它将在构建中使用) 可用于一次启动多台服务器或并行运行构建步骤。

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."
    }
}

示例输出:

sample output

答案 8 :(得分:0)

这几天有很多答案:

  1. 职位(或PS 6或模块中的threadjobs)
  2. 启动过程
  3. 工作流程
  4. 具有另一个运行空间的powershell api
  5. 具有多台计算机的调用命令,这些计算机都可以是localhost

这是foreach -parallel:

workflow work {
  foreach -parallel ($i in 1..3) { 
    sleep 5 
    "$i done" 
  }
}

work

3 done
1 done
2 done