RunSpace и его закрытие - PullRequest
1 голос
/ 17 января 2020

При работе со сценарием, использующим RunSpace, я обнаружил, что он занимает все больше системной памяти. Насколько я понимаю, это связано с тем, что открытые 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 по завершении. Спасибо тебе

1 Ответ

2 голосов
/ 17 января 2020

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

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

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

Примечание: Register-ObjectEvent необходимо использовать для подписки на событие; в то время как в принципе можно подписаться, передав блок скрипта непосредственно в .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.
...