Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/powershell/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading Powershell能否并行运行命令?_Multithreading_Powershell_Parallel Processing - Fatal编程技术网

Multithreading Powershell能否并行运行命令?

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

我有一个powershell脚本来对一堆图像进行批处理,我想进行一些并行处理。Powershell似乎有一些后台处理选项,如启动作业、等待作业等,但我发现用于并行工作的唯一好资源是编写脚本文本并运行这些()

理想情况下,我想要类似于.NET4中并行foreach的东西

一些看起来很简单的东西,比如:

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

这些天有很多答案:

  • 作业(或PS 6/7中的线程作业或PS 5的模块)
  • 启动过程
  • 工作流程(仅限PS 5)
  • 具有另一个运行空间的powershell api
  • 对多台计算机调用命令,这些计算机都可以是localhost(必须是admin)
  • ISE中的多个会话(运行空间)选项卡或远程powershell ISE选项卡
  • Powershell 7有一个
    foreach对象-并行
    作为#4的替代方案
  • 以下是foreach-parallel的工作流:

    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
    另请注意,如果在作业完成之前调用
    接收作业