PowerShell / PowerShell/PowerShell

`ForEach-Object -Parallel`: consider changing the semantics of the `-TimeoutSeconds` parameter to apply to each thread or provide a new switch / parameter that does

未关闭
#20,197 2 条评论 4 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

Issue-Enhancement Needs-Triage
主要语言
C#
星标
55.5k
派生
8.5k
平均合并
1 天 2 小时
30 天内合并 PR
88

描述

Summary of the new feature / enhancement

The current semantics of the -TimeoutSeconds parameter are arguably unhelpful:

  • The -TimeoutSeconds interval is applied to the runtime duration of the overall, typically throttled and therefore "batched" invocation[1] rather than to the runtime of each thread.

    • Therefore, a timeout can occur even if each individual thread completed in less than the specified timeout. A simple example:
          # This times out when the 3rd thread runs, because - due to ThrottleLimit 2 - 
          # it only starts after 1.5+ seconds, after the first 2 threads
          # have finished.
          1..4 | 
            ForEach-Object -TimeoutSeconds 2 -ThrottleLimit 2 -Parallel { 
              Start-Sleep -MilliSeconds 1500
              $_ # Pass the input object through.
            }
  • When a timeout occurs, the command terminates overall.

    • This means that threads for any remaining pipeline input then never even get to launch.
      • In the above example, only 1 and 2 print; input 4 never gets processed, because processing of 3 caused the timeout and therefore termination of the entire command.

Applying the specified timeout:

  • on a per-thread basis...
  • ... and continuing processing of the inputs even if a single thread times out

is arguably much more sensible.

Therefore, I suggest implementing the latter:

  • Either: By simply changing the meaning of the current -TimeoutSeconds parameter, assuming it amounts to a bucket 3 change.

  • Or: By introducing a separate parameter / additional switch parameter as an opt-in to the proposed semantics.


[1] More accurately, only a fixed number of threads are allowed to run at a time, based on the -ThrottleLimit arguments, which defaults to 5. If more threads are needed, they have to wait until "slots" open up, which happens when currently executing threads finish.

Proposed technical implementation details (optional)

The following shows the desired semantics expressed in PowerShell code, applied to a slightly modified version of the example above, which provokes a timeout for the 3rd input object:

# Use -AsJob to receive a job that allows monitoring the threads individually.
# Note that -AsJob cannot be combined with -TimeoutSeconds
$job = 
  1..4 | 
    ForEach-Object -AsJob -ThrottleLimit 2 -Parallel { 
      if ($_ -eq 3) {
        # Provoke a timeout error for this specific input.
        Start-Sleep -MilliSeconds 2500; $_
      } else {
        Start-Sleep -MilliSeconds 1500; $_
      }
    }

# Receive job output in a polling loop, and terminate child jobs
# that have run too long.
$timeout = 2
do {
  Start-Sleep -Milliseconds 500 # Sleep a little.
  # Get pending results.
  $job | Receive-Job
  # If any child jobs have been running for more than N seconds,
  # stop (terminate) them.
  # This will open up slots for more threads to spin up.
  foreach ($childJob in $job.ChildJobs.Where({ $_.State -eq 'Running' })) {
    if (([datetime]::now - $childJob.PSBeginTime).TotalSeconds -ge $timeout) {
      Write-Verbose -Verbose "Stopping job with ID $($childJob.Id) due to running longer than $timeout seconds..."
      $childJob | Stop-Job
    }
  }
} while ($job.ChildJobs.Where({ $_.State -in 'NotStarted', 'Running' }))

Output:

1
2
4
VERBOSE: Stopping job with ID 4 due to running longer than 2 seconds...

Note:

  • Input 4 was still processed, despite the thread for input 3 having timed out.

  • The ID value of the child job isn't really meaningful except to distinguish it from other child jobs; if you want to know what input object caused the timeout, you'll have to echo it as part of the script block (at the start, before a timeout can occur) - the job object doesn't contain this information.

贡献指南

打开贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

调研方向

首先,使用 -TimeoutSeconds-ThrottleLimit-AsJob 重现 ForEach-Object -Parallel 示例。将当前的整体超时行为与提议的按线程行为进行比较,包括一个线程超时后继续处理的情况。当现有参数具有新的语义,或有一个文档化的 opt-in 参数提供这些语义,并且覆盖受限流控制的输入和已超时的线程时,即视为完成。

由索引模型根据 Issue 内容生成。

评估

技术栈
powershell
领域
cli
Issue 类型
功能
难度
5/5
预计耗时
一周以上
活跃度
停滞
描述清晰度
基本清楚
新手友好度
35/100

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。