Удаление пространства выполнения в Powershell (зависит от графического интерфейса пользователя)

Я активно читаю StackOverflow, так как он обычно помогает мне решать все мои проблемы.
Но в качестве первого вопроса я прошу вашей помощи о пространствах выполнения в Powershell, особенно для управления памятью.

Я создал несколько приложений PS с графическим интерфейсом и теперь использую пространства выполнения, чтобы избежать зависания графического интерфейса при выполнении больших операций. Моя проблема в том, что я не могу избавиться от своих пространств выполнения, когда они будут завершены.

Что касается приведенного ниже кода, это простой сценарий, пытающийся понять, как я могу избавиться от своих пространств выполнения. Я попытался включить уникальное пространство выполнения для мониторинга и удаления пространств выполнения приложений, я вдохновился на скрипт proxb (большой поклонник, кстати), но похоже, что он не работает.

Каждый раз, когда я запускаю пространство выполнения, я получаю 10 МБ памяти, огромная утечка!

утечка

Не могли бы вы помочь мне решить эту проблему?

[xml]$xaml = @'
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Runspace" FontFamily="Calibri" Height="400" Width="400" FontSize="14">
    <Grid Margin="10,10,10,10">
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="10"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <TextBox x:Name="TB_Results" Grid.Row="0" TextWrapping="Wrap" VerticalScrollBarVisibility="Auto"/>
        <Button x:Name="BT_Run" Grid.Row="2" Content="Run" Height="30"/>
    </Grid>
</Window>
'@

# Inits
Add-Type -AssemblyName PresentationFramework            
Add-Type -AssemblyName PresentationCore
Add-Type -AssemblyName WindowsBase
$gui = [hashtable]::Synchronized(@{})
$reader = (New-Object Xml.XmlNodeReader $xaml)
$gui.Window = [Windows.Markup.XamlReader]::Load($reader)
$xaml.SelectNodes("//*[@*[contains(translate(name(.),'n','N'),'Name')]]") | ForEach-Object { $gui.Add($_.Name,$gui.Window.FindName($_.Name)) }
$Script:Jobs = [system.collections.arraylist]::Synchronized((New-Object System.Collections.ArrayList))
$Script:JobCleanup = [hashtable]::Synchronized(@{ Host = $host })

# Test function
function RunFunc {
        $newRunspace = [runspacefactory]::CreateRunspace()
        $newRunspace.ApartmentState = "STA"
        $newRunspace.ThreadOptions = "ReuseThread"
        $newRunspace.Open()
        $newRunspace.SessionStateProxy.SetVariable("gui",$gui)
        $Code = {
            $memupdate = "Memory: $($(Get-Process -Id $PID).PrivateMemorySize64 / 1mb) mb"
            $gui.Window.Dispatcher.invoke("Normal",[action]{
                $gui.TB_Results.Text = "$memupdate`r`n" + $gui.TB_Results.Text
            })
        }
        $Powershell = [powershell]::Create().AddScript($Code)
        $Powershell.Runspace = $newRunspace
        [void]$Script:Jobs.Add((
            [PSCustomObject]@{
                PowerShell = $PowerShell
                Runspace = $PowerShell.BeginInvoke()
            }
        ))
}

# Background Runspace to clean up jobs
$newRunspace =[runspacefactory]::CreateRunspace()
$newRunspace.ApartmentState = "STA"
$newRunspace.ThreadOptions = "ReuseThread"          
$newRunspace.Open() 
$newRunspace.SessionStateProxy.SetVariable("jobs",$Script:Jobs) 
$Code = {
    $JobCleanup = $true
    do {    
        foreach($runspace in $jobs) {            
            if ($runspace.Runspace.isCompleted) {
                $runspace.powershell.EndInvoke($runspace.Runspace) | Out-Null
                $runspace.powershell.dispose()
                $runspace.Runspace = $null
                $runspace.powershell = $null               
            } 
        }
        $temphash = $jobs.clone()
        $temphash | Where-Object { $_.runspace -eq $Null } | ForEach-Object { $jobs.remove($_) }        
        Start-Sleep -Seconds 1     
    } while ($JobCleanup)
}
$Powershell = [powershell]::Create().AddScript($Code)
$Powershell.Runspace = $newRunspace
$PowerShell.BeginInvoke()

# gui events
$gui.BT_Run.add_Click({ RunFunc })
$gui.Window.ShowDialog() | Out-Null

person Carlton2001    schedule 11.08.2018    source источник


Ответы (1)


Когда вы вызываете $runspace.PowerShell.Dispose(), экземпляр PowerShell удаляется, но пространство выполнения, к которому он привязан, не удаляется автоматически, вам нужно сначала сделать это самостоятельно в задаче очистки:

$runspace.powershell.EndInvoke($runspace.Runspace) | Out-Null
$runspace.powershell.Runspace.Dispose() # remember to clean up the runspace!
$runspace.powershell.dispose()
person Mathias R. Jessen    schedule 11.08.2018
comment
@ Carlton2001 приятно слышать! Ради вас я бы предложил переименовать свойство Runspace в пользовательском объекте задания на Result или Handle, а затем изменить foreach($runspace in $jobs) на foreach($job in $jobs) - person Mathias R. Jessen; 11.08.2018