通过作业完成控制PowerShell多线程作业速度

5
我找到的所有教程都使用预定义的休眠时间来限制作业。 我需要限制器在等待作业完成之前暂停启动新作业。 一次只能运行4个作业。
因此,脚本将运行4个作业,当前暂停10秒钟,然后运行其余作业。 我想要的是脚本一次只允许运行4个作业,并且在作业完成后启动新作业。
作业是通过服务器名称列表初始化的。
这是否可能实现?
$servers = Get-Content "C:\temp\flashfilestore\serverlist.txt"

$scriptBlock = { #DO STUFF }


$MaxThreads = 4

foreach($server in $servers) {
     Start-Job -ScriptBlock $scriptBlock -argumentlist  $server 
     While($(Get-Job -State 'Running').Count -ge $MaxThreads) {
          sleep 10 #Need this to wait until a job is complete and kick off a new one.
     }
}
Get-Job | Wait-Job | Receive-Job
6个回答

4

您可以进行以下测试:

$servers = Get-Content "C:\temp\flashfilestore\serverlist.txt"
$scriptBlock = { #DO STUFF }
invoke-command -computerName $servers -scriptblock $scriptBlock -jobname 'YourJobSpecificName' -throttlelimit 4 -AsJob

此命令使用 Invoke-Command 命令和其 AsJob 参数,启动在多台计算机上运行脚本块的后台作业。由于命令不能超过 4 次并发运行,因此命令使用 Invoke-Command 的 ThrottleLimit 参数来限制并发命令的数量为 4。
请注意,文件中包含的计算机名称属于域名。

4
为了避免重复造轮子,我建议使用现有的工具之一。
其中之一是脚本Invoke-Parallel.ps1。它是用PowerShell编写的,您可以直接查看其实现方式。它易于获取,并且在使用时不需要安装。
另一个工具是模块SplitPipeline。它可能更快,因为它是用C#编写的。它还涵盖了更多的用例,例如缓慢或无限输入,使用初始化和清理脚本等。
在后一种情况下,带有4个并行管道的代码将是:
$servers | Split-Pipeline -Count 4 {process{ <# DO STUFF on $_ #> }}

3
我写了一篇博客文章,介绍了如何通过实际线程来实现任何给定脚本的多线程。您可以在此处找到完整的文章:http://www.get-blog.com/?p=189
基本设置如下:
$ISS = [system.management.automation.runspaces.initialsessionstate]::CreateDefault()
$RunspacePool = [runspacefactory]::CreateRunspacePool(1, $MaxThreads, $ISS, $Host)
$RunspacePool.Open()

$Code = [ScriptBlock]::Create($(Get-Content $FileName))
$PowershellThread = [powershell]::Create().AddScript($Code)

$PowershellThread.RunspacePool = $RunspacePool
$Handle = $PowershellThread.BeginInvoke()
$Job = "" | Select-Object Handle, Thread, object
$Job.Handle = $Handle
$Job.Thread = $PowershellThread
$Job.Object = $Object.ToString()

$Job.Thread.EndInvoke($Job.Handle)
$Job.Thread.Dispose()

不是一个坏链接。我更喜欢使用runspaces,因为它们占用的资源较少。 - xXhRQ8sD2L7Z
嗨,Ryan,你的博客还在吗?这个URL已经无法访问了 :( - FoxDeploy

1

你可以等待一个作业(-any作业),而不是使用sleep 10

Get-Job | Wait-Job -Any | Out-Null

当没有更多的任务可以启动时,开始打印输出。您也可以在上述命令之后立即在循环内部执行此操作。脚本将在完成作业时接收作业,而不是等到结束。
Get-Job -State Completed | % {
   Receive-Job $_ -AutoRemoveJob -Wait
}

所以你的脚本应该是这样的:

$servers = Get-Content "C:\temp\flashfilestore\serverlist.txt"

$scriptBlock = { #DO STUFF }

$MaxThreads = 4

foreach ($server in $servers) {
   Start-Job -ScriptBlock $scriptBlock -argumentlist $server 
   While($(Get-Job -State Running).Count -ge $MaxThreads) {
      Get-Job | Wait-Job -Any | Out-Null
   }
   Get-Job -State Completed | % {
      Receive-Job $_ -AutoRemoveJob -Wait
   }
}
While ($(Get-Job -State Running).Count -gt 0) {
   Get-Job | Wait-Job -Any | Out-Null
}
Get-Job -State Completed | % {
   Receive-Job $_ -AutoRemoveJob -Wait
}

话虽如此,我更喜欢使用runspaces(类似于Ryan的帖子)甚至是工作流程(如果可以使用的话)。这些比启动多个PowerShell进程要少得多的资源。


0
你的脚本看起来不错,试着添加类似下面这样的内容:

Write-Host ("当前计数:" + ($(Get-Job -State 'Running').Count) + " 在服务器上:" + $server)

在 while 循环之后,以确定作业计数是否正在下降,而您并不希望它这样做。

0
我注意到每次运行 Start-Job 命令都会在任务管理器中增加一个额外的 conhost.exe 进程。基于这一发现,我可以采用以下逻辑来限制线程数。如果我想要同时运行的线程数量为 5,则我使用 -gt 语句时输入 4 来表示进程数量应该大于 4。
while((Get-Process conhost -ErrorAction SilentlyContinue).Count -gt 4){Start-Sleep -Seconds 1}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接