[英]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.