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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-10 01:11:08  来源:igfitidea点击:

Can Powershell Run Commands in Parallel?

multithreadingpowershellparallel-processing

提问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 -ArgumentListparameter on Start-Jobto send it and use paraminside the script block to receive it.

要将变量从父上下文传递到子上下文,请使用-ArgumentListon 参数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:

这几天有很多答案:

  1. jobs (or threadjobs in PS 6/7 or the module)
  2. start-process
  3. workflows
  4. powershell api with another runspace
  5. invoke-command with multiple computers, which can all be localhost (have to be admin)
  6. multiple session (runspace) tabs in the ISE, or remote powershell ISE tabs
  7. Powershell 7 has a foreach-object -parallelas an alternative for #4
  1. 作业(或 PS 6/7 或模块中的线程作业)
  2. 启动进程
  3. 工作流程
  4. 带有另一个运行空间的 powershell api
  5. 多台计算机的调用命令,都可以是本地主机(必须是管理员)
  6. ISE 中的多个会话(运行空间)选项卡,或远程 powershell ISE 选项卡
  7. 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-Jobto 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