繁体   English   中英

PowerShell 上的异步代码执行?

[英]asynchronous code execution on PowerShell?

请帮我弄清楚异步循环执行是如何工作的。 很多论坛里的例子很繁琐,看不懂

在执行过程中,程序 window 冻结,并在循环结束时显示最终编号

例如

   [void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
[xml]$XAML = @"
 
<Window 
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 
 
        Title="Рассылка интернет-трафика" Height="200" Width="300" ResizeMode="NoResize">
    <Grid Width="300">
        <TextBox Name="text_result" TextWrapping="Wrap" Margin="0,0,0,0" VerticalAlignment="Top" Height="31" Width="200" />
        <Button Name="button_start" Content="Start" Height="31" Margin="0,50,0,0" VerticalAlignment="Top" Width="200" />
        <Button Name="button_close" Content="Close"  Height="31" Margin="0,100,0,0" VerticalAlignment="Top" Width="200"/>


    </Grid>
</Window>
 
 
"@
#Read XAML
$reader=(New-Object System.Xml.XmlNodeReader $xaml) 
try{$Form=[Windows.Markup.XamlReader]::Load( $reader )}
catch{Write-Host "Unable to load Windows.Markup.XamlReader"; exit}
 
# Store Form Objects In PowerShell
$xaml.SelectNodes("//*[@Name]") | ForEach-Object {Set-Variable -Name ($_.Name) -Value $Form.FindName($_.Name)}


    $button_start.Add_Click({
        for ($row = 2; $row -le 1000; $row++) {
            $text_result.Text = $row
        }
    })

    #Загружаем главную форму
    $Form.ShowDialog() | out-null

我花了很长时间才弄清楚这一点。 让我先说自动机亚当有一个奇妙的故障:

https://adamtheautomator.com/building-asynchronous-powershell-functions/

考虑执行异步循环的最简单方法是考虑欺骗 powershell。 简而言之,您只希望它在脚本运行时在后台执行某些操作。 所以,你只需要将你想要的循环变成一个脚本块并使用start-job ,又名&

让我们使用一个常见的任务,例如查看一堆文件。 在这里,我们将使用 gci。

$scriptblock = {
param ($folder)
Get-ChildItem -Path $folder}

Start-job -ScriptBlock $scriptblock -ArgumentList "C:\Windows" -name "stackexample"

该名称很重要,以便您以后可以引用它。 现在您在后台有一份工作,您可以继续编写脚本。 但是,如果您想查看工作是否完成,会发生什么? 有一个用于检查工作状态的命令(下面的代码)。

Get-Job -Name stackexample

需要注意的一件事是,一旦完成,请确保关闭您的工作。

$status = (Get-Job -Name stackexample).state

if ($status -eq "completed")
{
    Remove-Job -Name stackexample
}

等等,我需要知道那些文件夹是什么。 没问题,我们只需要告诉 scriptblock 到 output 数据(例如 write-host),然后。 我们在关闭它之前收到了工作:所以一个完整的电路可能看起来像这样:

$scriptblock = {
    param ($folder)
    $folders = Get-ChildItem -Path $folder
    write-host $folders
}

Start-job -ScriptBlock $scriptblock -ArgumentList "C:\Windows" -name "stackexample"

Start-Sleep -Seconds 5

$status = (Get-Job -Name stackexample).state

if ($status -eq "completed")
{
    Receive-Job -Name StackExample -Wait
    Remove-Job -Name stackexample
}

您可以从这里获得更多精彩,但这就是“异步代码执行”的核心 - 告诉 powershell 在您做其他事情时做一些耗时的事情。

应用到您的代码中,您可以使用按钮单击来启动作业并在后台运行,然后接收最终结果。

$xaml.SelectNodes("//*[@Name]") | ForEach-Object {Set-Variable -Name ($_.Name) -Value $Form.FindName($_.Name)}

$scriptblock = {
    for ($row = 2; $row -le 1000; $row++) 
    {
        $result = $row
            
    }
    return $result
}

$button_start.Add_Click({
    Start-job -ScriptBlock $scriptblock -Name scriptblock
    While ((Get-Job -Name scriptblock).State -ne "Completed")
    {
        start-sleep 1
    }

    $results = receive-job -Name scriptblock -keep
    remove-job -Name scriptblock
    $text_result.Text = $results
})

在按钮单击上方,我们已经定义了脚本块。 单击按钮时,它会启动作业,然后使用 while 循环监视它。 工作完成后,我们可以提取结果(在此处使用脚本块中的return $result完成)。 收到这份工作,你就有了你现在可以为用户显示的最终变量。

没有循环。

在这种情况下,表单只是一个名称。 如果需要,您可以从中删除所有 GUI 元素。

$Script:SyncHashTable = [Hashtable]::Synchronized(@{})
$RunSpace = [Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace()
$RunSpace.ApartmentState = "STA"
$RunSpace.ThreadOptions = "ReuseThread"
$RunSpace.Open()
$RunSpace.SessionStateProxy.SetVariable("SyncHashTable", $Script:SyncHashTable)
$PowerShellCmd = [Management.Automation.PowerShell]::Create().AddScript({
    [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
    [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
    
    $Script:SyncHashTable.objForm = New-Object System.Windows.Forms.Form
    $Script:SyncHashTable.InitialFormWindowState = New-Object System.Windows.Forms.FormWindowState
    $Script:SyncHashTable.objForm.Size = New-Object System.Drawing.Size(340, 200)
    $Script:SyncHashTable.objForm.Name = "objForm"
    $Script:SyncHashTable.objForm.Text = "Test form"
    
    $Script:SyncHashTable.objLabel = New-Object System.Windows.Forms.Label
    $Script:SyncHashTable.objLabel.Location = New-Object System.Drawing.Size(10,20)
    $Script:SyncHashTable.objLabel.Size = New-Object System.Drawing.Size(320,20)
    $Script:SyncHashTable.objLabel.Text = "Enter the data and press the OK button or key Enter"
    $Script:SyncHashTable.objForm.Controls.Add($Script:SyncHashTable.objLabel)
    
    $Script:SyncHashTable.objTextBox = New-Object System.Windows.Forms.TextBox
    $Script:SyncHashTable.objTextBox.Location = New-Object System.Drawing.Size(10,40)
    $Script:SyncHashTable.objTextBox.Size = New-Object System.Drawing.Size(300,20)
    $Script:SyncHashTable.objForm.Controls.Add($Script:SyncHashTable.objTextBox)
    
    $Script:SyncHashTable.objForm.KeyPreview = $True
    $Script:SyncHashTable.objForm.Add_KeyDown( { if ($_.KeyCode -eq "Enter") { $Script:SyncHashTable.X = $Script:SyncHashTable.objTextBox.Text;$Script:SyncHashTable.objForm.Close() } })
    $Script:SyncHashTable.objForm.Add_KeyDown( { if ($_.KeyCode -eq "Escape") { $objForm.Close() } })
    
    $Script:SyncHashTable.OKButton = New-Object System.Windows.Forms.Button
    $Script:SyncHashTable.OKButton.Location = New-Object System.Drawing.Size(75,120)
    $Script:SyncHashTable.OKButton.Size = New-Object System.Drawing.Size(75,23)
    $Script:SyncHashTable.OKButton.Text = "OK"
    $Script:SyncHashTable.OKButton.Add_Click( { $Script:SyncHashTable.X = $Script:SyncHashTable.objTextBox.Text;$Script:SyncHashTable.objForm.Close() } )
    $Script:SyncHashTable.objForm.Controls.Add($Script:SyncHashTable.OKButton)
    
    $Script:SyncHashTable.InitialFormWindowState = $Script:SyncHashTable.objForm.WindowState
    $Script:SyncHashTable.objForm.add_Load($Script:SyncHashTable.OnLoadForm_StateCorrection)
    [void] $Script:SyncHashTable.objForm.ShowDialog()
    })
$PowerShellCmd.Runspace = $RunSpace
$obj=$PowerShellCmd.BeginInvoke()
#Time to retrieve our missing object $AsyncObject
$BindingFlags = [Reflection.BindingFlags]'nonpublic','instance'
$Field = $PowerShellCmd.GetType().GetField('invokeAsyncResult',$BindingFlags)
$AsyncObject = $Field.GetValue($PowerShellCmd)
Write-Host 'Here is your code that will be executed in parallel with the form code'
Write-Host '(any length and execution time)'
Write-Host "`$PowerShellCmd.EndInvoke(`$AsyncObject) will wait for results from the form,"
Write-Host "or pick them up if they are ready."
Write-Host "=================================="
#Closing the execution space when getting the result
$PowerShellCmd.EndInvoke($AsyncObject)
'This is the result of executing the form code: {0}' -f $Script:SyncHashTable.X

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM