1

我正在尝试在 powershell 中执行函数或脚本块并设置执行超时。

基本上我有以下(翻译成伪代码):

function query{
    #query remote system for something
}

$computerList = Get-Content "C:\scripts\computers.txt"

foreach ($computer in $computerList){
    $result = query
    #do something with $result
}

查询的范围可以从使用 Get-WmiObject 的 WMI 查询到 HTTP 请求,并且脚本必须在混合环境中运行,其中包括不都具有 HTTP 接口的 Windows 和 Unix 机器。因此,某些查询必然会挂起或需要很长时间才能返回。在我寻求优化的过程中,我写了以下内容:

$blockofcode = {
    #query remote system for something
}

foreach ($computer in $computerList){
    $Job = Start-Job -ScriptBlock $blockofcode -ArgumentList $computer
    Wait-Job $Job.ID -Timeout 10 | out-null
    $result = Receive-Job $Job.ID
    #do something with result
}

但不幸的是,工作似乎会带来很多开销。在我的测试中,在 1.066 秒内执行的查询(根据 $blockofcode 中的计时器)在作为作业执行时需要 6.964 秒才能返回结果。当然可以,但我真的很想减少开销。我也可以一起开始所有工作,然后等待它们完成,但这些工作仍然会挂起或花费大量时间来完成。

所以,关于这个问题:有没有办法执行一个语句、函数、脚本块甚至是一个超时的脚本,它不包括作业带来的那种开销?如果可能的话,我想并行运行这些命令,但这不是一个交易破坏者。

任何帮助或提示将不胜感激!

编辑:在混合 windows/unix 环境中运行 powershell V3

4

2 回答 2

4

今天,我遇到了一个类似的问题,并注意到这个问题没有真正的答案。我创建了一个简单的 PowerShell 类,名为TimedScript. 此类提供以下功能:

  • Method :Start()当你准备好时开始工作的方法
  • Method :GetResult()方法,检索脚本的输出
  • 构造函数:一个带有两个参数的构造函数:
    • ScriptBlock执行
    • [int]超时时间,以毫秒为单位

它目前缺乏:

  • 将参数传递给 PowerShell ScriptBlock
  • 您想出的其他有用功能

类:定时脚本

class TimedScript {
  [System.Timers.Timer] $Timer = [System.Timers.Timer]::new()
  [powershell] $PowerShell
  [runspace] $Runspace = [runspacefactory]::CreateRunspace()
  [System.IAsyncResult] $IAsyncResult

  TimedScript([ScriptBlock] $ScriptBlock, [int] $Timeout) {    
    $this.PowerShell = [powershell]::Create()
    $this.PowerShell.AddScript($ScriptBlock)
    $this.PowerShell.Runspace = $this.Runspace

    $this.Timer.Interval = $Timeout

    Register-ObjectEvent -InputObject $this.Timer -EventName Elapsed -MessageData $this -Action ({
      $Job = $event.MessageData
      $Job.PowerShell.Stop()
      $Job.Runspace.Close()
      $Job.Timer.Enabled = $False
    })
  }

  ### Method: Call this when you want to start the job.
  [void] Start() {
    $this.Runspace.Open()
    $this.Timer.Start()
    $this.IAsyncResult = $this.PowerShell.BeginInvoke()
  }

  ### Method: Once the job has finished, call this to get the results
  [object[]] GetResult() {
    return $this.PowerShell.EndInvoke($this.IAsyncResult)
  }
}

TimedScript 类的示例用法

# EXAMPLE: The timeout period is set longer than the execution time of the script, so this will succeed
$Job1 = [TimedScript]::new({ Start-Sleep -Seconds 2 }, 4000)

# EXAMPLE: This script will fail. Even though Get-Process returns quickly, the Start-Sleep call will cause it to be terminated by its Timer.
$Job2 = [TimedScript]::new({ Get-Process -Name s*; Start-Sleep -Seconds 3 }, 2000)

# EXAMPLE: This job will fail, because the timeout is less than the script execution time.
$Job3 = [TimedScript]::new({ Start-Sleep -Seconds 3 }, 1000)

$Job1.Start()
$Job2.Start()
$Job3.Start()

代码也托管在GitHub Gist 上

于 2018-01-09T23:24:17.757 回答
1

我认为您可能想使用 Powershell 运行空间进行调查:

http://learn-powershell.net/2012/05/13/using-background-runspaces-instead-of-psjobs-for-better-performance/

于 2013-11-08T11:35:45.243 回答