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 摘要。