【问题标题】:Timers.Timer paralyze scriptTimers.Timer 瘫痪脚本
【发布时间】:2021-11-18 11:08:24
【问题描述】:

我正在尝试运行this 示例:

Clear-Host
$timer = [Timers.Timer]::new()
$timer.Interval = 200
$action = {
    if($complete -eq 8){
        write-host "completed $complete" -ForegroundColor Red
        write-host "Timer stopped" -ForegroundColor Red
        $timer.stop()
        Unregister-Event thetimer
    }
}
Register-ObjectEvent -InputObject $timer -EventName elapsed `
–SourceIdentifier  thetimer -Action $action

$complete = 0
$timer.Start()

while($true){
    Write-Host "Script working: $complete" -ForegroundColor Green
    Start-Sleep -m 400
    $complete++
}

但是当定时器停止时,脚本就瘫痪了。

我做错了什么,如何解决?

【问题讨论】:

    标签: .net powershell


    【解决方案1】:
    • 除非您直接在 global 范围内执行它(通过在提示符处粘贴它或通过 dot-sourcing 脚本包含它)。

      • 原因是传递给Register-ObjectEvent-Action 参数的脚本块在动态模块 中运行,它只看到全局 范围的变量,而不是任何其他调用者的变量,例如脚本的变量。
    • 如果您确实从全局范围运行,则 挂起是由在 -Action 脚​​本块内调用 Unregister-Event 引起的

      • 改为从 调用者的范围调用它(即从调用 Register-ObjectEvent 的同一范围)。

      • 当从-Action 脚本块内部调用代码时挂起(至少发生在 PowerShell 7.1,在撰写本文时的最新版本)应该被视为一个 错误;在调用Unregister-Event有更多事件待处理,这似乎会发生;该问题已在GitHub issue #16154 中报告

    以下是一个独立的工作示例,您也可以从脚本中调用它:

    $timer = [Timers.Timer]::new()
    $timer.Interval = 200
    
    $action = {
      # Note: This script block generally does NOT see the caller's variables.
      $timer = $Event.Sender  # originating timer object
      $counter = $Event.MessageData.Value # the variable object passed to -MessageData
      # Note: Since you're only *getting* and *integer* value, you needn't worry
      #       about thread synchronization.
      if ($timer.Enabled -and $counter -ge 8) { 
          write-host "completed $counter" -ForegroundColor Red
          write-host "Timer stopped" -ForegroundColor Red
          # Refer to the originating timer object with $Event.Sender or $args[0]
          $timer.stop()
          # Do NOT try to call Unregister-Event here - it'll cause a hang.
      }
    }
    
    # Initialize the counter variable.
    $complete = 0
    
    # Get the variable as a variable *object* that can be passed to the -Action
    # script block with -MessageData.
    $counterVarObject = Get-Variable complete
    
    # Register the event and pass the variable object with -MessageData.
    $null = Register-ObjectEvent -InputObject $timer -EventName elapsed `
                                 –SourceIdentifier thetimer `
                                 -Action $action -MessageData $counterVarObject
    
    $timer.Start()
    
    try {
      while ($timer.Enabled) { # Loop until the timer stops.
          Write-Host "Script working: $complete" -ForegroundColor Green
          Start-Sleep -m 400
          $complete++
      }
    }
    finally { # This is called even if the loop is aborted with Ctrl-C.
      # Unregister the event from the *caller's scope.
      Unregister-Event theTimer
    }
    

    【讨论】:

      猜你喜欢
      • 2021-10-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-22
      • 2011-02-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多