RunSpace и его закрытие

Работая со сценарием, использующим RunSpace, я обнаружил, что он занимает все больше и больше системной памяти. Насколько я понимаю, это связано с тем, что open RunSpace не закрывается по завершении. Они остаются в памяти, накапливая мегабайты.

Как правильно закрыть RunSpace? Однако я не знаю, сколько времени это займет - 1 секунда или 1 час. По завершении закрывается.

В качестве примера приведу произвольные скрипты.

Первый сценарий - это то, как я закрываю RunSpace по мере его завершения (а он, по-видимому, не работает).

$Array = 1..10000

$PowerShell = [PowerShell]::Create()
$RunSpace = [Runspacefactory]::CreateRunspace()
$RunSpace.Open()

$RunSpace.SessionStateProxy.SetVariable('Array', $Array)
$RunSpace.SessionStateProxy.SetVariable('PowerShell', $PowerShell)

$PowerShell.Runspace = $RunSpace

[void]$PowerShell.AddScript({

   # Fill the system memory so that it can be seen in the Task Manager.
   $Array += $Array
   $Array

   # Closing the Process, which should close the RunSpace, but this does not happen.
   $Powershell.Runspace.Dispose()
   $PowerShell.Dispose()
})

$Async = $PowerShell.BeginInvoke()

# Other jobs in the main thread...

Второй сценарий кажется более правильным, судя по системной памяти. Однако в жизни это, конечно, неприменимо, так как Start-Sleep 10 замораживает основной процесс.

$Array = 1..10000

$PowerShell = [PowerShell]::Create()
$RunSpace = [Runspacefactory]::CreateRunspace()
$RunSpace.Open()

$RunSpace.SessionStateProxy.SetVariable('Array', $Array)

$PowerShell.Runspace = $RunSpace

[void]$PowerShell.AddScript({

   # Fill the system memory so that it can be seen in the Task Manager.
   $Array += $Array
   $Array
})

$Async = $PowerShell.BeginInvoke()

Start-Sleep 10

$PowerShell.EndInvoke($Async) | Out-Null
$PowerShell.RunSpace.Dispose()
$PowerShell.Dispose()

# Other jobs in the main thread...

Пожалуйста, напишите мне, как правильно закрыть RunSpace по мере его завершения. Спасибо вам


person Кирилл Зацепин    schedule 17.01.2020    source источник


Ответы (1)


Попытка удалить пространство выполнения из внутри этого пространства выполнения звучит как плохая идея - на самом деле, если я попробую это в PowerShell Core 7.0-rc2, мой сеанс зависнет.

Похоже, вы хотите удалить пространство выполнения автоматически, по завершении скрипта.

Вы можете настроить обработчик событий для System.Management.Automation.PowerShell.InvocationStateChanged , внутри которого вы можете проверить состояния Completed и Failed и закрыть пространство выполнения, а затем:

Примечание. Для подписки на мероприятие; хотя в принципе можно подписаться, передав блок сценария непосредственно .add_InvocationStateChanged() в экземпляре PowerShell, выполнение будет зависать, когда вы позже вызовете .EndInvoke().

# Note: I'm using a small array so that the output of the code better
#       shows what's happening.
$Array = 1..3

$PowerShell = [PowerShell]::Create()
$RunSpace = [Runspacefactory]::CreateRunspace()
$RunSpace.Open()

$RunSpace.SessionStateProxy.SetVariable('Array', $Array)

$PowerShell.Runspace = $RunSpace

$null = $PowerShell.AddScript( {

    # Fill the system memory so that it can be seen in the Task Manager.
    $Array += $Array
    $Array

  })

# Set up an event handler for when the invocation state of the runspace changes.
# Note: Register-ObjectEvent with -Action returns an event-job object, which
#       we don't need in this case.
$null = Register-ObjectEvent -InputObject $PowerShell -EventName InvocationStateChanged -Action {
  param([System.Management.Automation.PowerShell] $ps)

  # NOTE: Use $EventArgs.InvocationStateInfo, not $ps.InvocationStateInfo, 
  #       as the latter is updated in real-time, so for a short-running script
  #       the state may already have changed since the event fired.
  $state = $EventArgs.InvocationStateInfo.State

  Write-Host "Invocation state: $state"
  if ($state -in 'Completed', 'Failed') {
    # Dispose of the runspace.
    Write-Host "Disposing of runspace."
    $ps.Runspace.Dispose()
    # Speed up resource release by calling the garbage collector explicitly.
    # Note that this will pause *all* threads briefly.
    [GC]::Collect()
  }      

}

# Kick off execution of the script and
# let the event handler dispose of the runspace on completion.
Write-Host 'Starting script execution via SDK...'
$asyncResult = $PowerShell.BeginInvoke()

# Perform other processing.
Write-Host 'Doing things...'
1..1e5 | ForEach-Object { }

# Finally, get the results from the script execution.
# NOTE: Even though the runspace has likely already been disposed, collecting
#       the results seemingly still works.
Write-Host 'Collecting script-execution results...'
$PowerShell.EndInvoke($asyncResult)

# Note that you'll have to create and assign a *new* runspace to 
# $PowerShell.Runspace if you want to execute further code.

Write-Host 'Done.'

Вышеупомянутое должно дать следующий результат:

Starting script execution via SDK...
Doing things...
Invocation state: Running
Invocation state: Completed
Disposing of runspace.
Collecting script-execution results...
1
2
3
1
2
3
Done.
person mklement0    schedule 17.01.2020