简体   繁体   English

PowerShell 上的异步代码执行?

[英]asynchronous code execution on PowerShell?

Help me please figure out how asynchronous loop execution works.请帮我弄清楚异步循环执行是如何工作的。 In many forums, examples are very cumbersome and not understandable很多论坛里的例子很繁琐,看不懂

During execution, the program window freezes, and at the end of the cycle it shows the final number在执行过程中,程序 window 冻结,并在循环结束时显示最终编号

for example例如

   [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

It took me quite a while to figure this one out.我花了很长时间才弄清楚这一点。 Let me preface by saying that Adam the Automator has a fantastic breakdown:让我先说自动机亚当有一个奇妙的故障:

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

The easiest way to think about executing an asynchronous loop is to think about tricking powershell.考虑执行异步循环的最简单方法是考虑欺骗 powershell。 In short, you just want it to do something in the background while your script is running.简而言之,您只希望它在脚本运行时在后台执行某些操作。 So, you just need to turn the loop you would like into a script block and use start-job , aka & .所以,你只需要将你想要的循环变成一个脚本块并使用start-job ,又名&

Let's use a common task, such as looking through a bunch of files.让我们使用一个常见的任务,例如查看一堆文件。 Here, we'll use gci.在这里,我们将使用 gci。

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

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

The name is important so that you can reference it later.该名称很重要,以便您以后可以引用它。 Now you have a job in the background, and you're good to continue with your script.现在您在后台有一份工作,您可以继续编写脚本。 But what happens if you want to see if the job is done?但是,如果您想查看工作是否完成,会发生什么? There's a command for checking on the status of your job (code below).有一个用于检查工作状态的命令(下面的代码)。

Get-Job -Name stackexample

One thing to note, make sure to CLOSE your jobs once they're done.需要注意的一件事是,一旦完成,请确保关闭您的工作。

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

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

Wait, I need to know what those folders were.等等,我需要知道那些文件夹是什么。 No problem, We just need to tell the scriptblock to output data (write-host. for example), Then.没问题,我们只需要告诉 scriptblock 到 output 数据(例如 write-host),然后。 we receive the job before we close it: So a full circuit might look like this:我们在关闭它之前收到了工作:所以一个完整的电路可能看起来像这样:

$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
}

You can get so much fancier from here, but that is the core of what 'asynchronous code execution' is - telling powershell to do something time consuming while you're doing other stuff.您可以从这里获得更多精彩,但这就是“异步代码执行”的核心 - 告诉 powershell 在您做其他事情时做一些耗时的事情。

As applied to your code, you can use the button click to set off a job and run in the background, then receive the final results.应用到您的代码中,您可以使用按钮单击来启动作业并在后台运行,然后接收最终结果。

$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
})

Above the button click we've defined the script block.在按钮单击上方,我们已经定义了脚本块。 When the button is clicked, it kicks off the job and then watches it with the while loop.单击按钮时,它会启动作业,然后使用 while 循环监视它。 Once the job is finished, we can extract the result (done here with return $result in the scriptblock).工作完成后,我们可以提取结果(在此处使用脚本块中的return $result完成)。 Receive the job and you have your final variable which you can now display for the user.收到这份工作,你就有了你现在可以为用户显示的最终变量。

With no cycle.没有循环。

The form is just a name in this case.在这种情况下,表单只是一个名称。 You can remove all GUI elements from it if you want.如果需要,您可以从中删除所有 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