乔布斯真的在Powershell的后台工作吗? [英] Do Jobs really work in background in powershell?

查看:57
本文介绍了乔布斯真的在Powershell的后台工作吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试实现在用户单击按钮后启动的后台命令,到目前为止,我一直都在工作进行中最终锁定了UI.我目前在使用一些比较耗时的for循环来检查我的UI是否锁定.当UI释放后,我该怎么做才能使工作在后台运行.我不确定是否要通过添加运行空间来使代码复杂化.我在做什么错?

I am trying to implement background commands that would initiate after the user clicks a button, and so far I have always ended up with my UI locking up while the job is on-going. I am currently using a somewhat time consuming for loop to check if my UI locks up. What can I do to let the job work in the background as the UI is freed up. I am not sure I want to complicate the code by adding runspaces. What am I doing incorrectly?

$inputXML = @"
<Window x:Class="WpfApplication2.MainWindow"
        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"
        xmlns:local="clr-namespace:WpfApplication2"
        mc:Ignorable="d"
        Title="Create User" Height="448.05" Width="656.017" ResizeMode="NoResize">
    <Grid Margin="0,0,-6.8,-0.8" Background="#FFD7D7D7">
        <Grid.RowDefinitions>
            <RowDefinition Height="403*"/>
            <RowDefinition Height="18*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Button x:Name="Create_User" Content="Create User" HorizontalAlignment="Left" Margin="67,224,0,0" VerticalAlignment="Top" Width="300" Height="26" RenderTransformOrigin="0.551,-0.671" IsEnabled="True">
            <Button.Background>
                <LinearGradientBrush EndPoint="0,1" StartPoint="0,0">
                    <GradientStop Color="#FFF3F3F3" Offset="0"/>
                    <GradientStop Color="#FFEBEBEB" Offset="0.5"/>
                    <GradientStop Color="#FFDDDDDD" Offset="0.5"/>
                    <GradientStop Color="#FFCDCDCD" Offset="1"/>
                </LinearGradientBrush>
            </Button.Background>
        </Button>
        <Label x:Name="fname" Content="" HorizontalAlignment="Left" Margin="43,11,0,0" VerticalAlignment="Top" Height="26" Width="10"/>
        <Label x:Name="fname1" Content="First Name" HorizontalAlignment="Left" Margin="88,54,0,0" VerticalAlignment="Top" Width="72" RenderTransformOrigin="0.513,1.469" Height="26"/>
        <Label x:Name="lname" Content="Last Name" HorizontalAlignment="Left" Margin="88,83,0,0" VerticalAlignment="Top" RenderTransformOrigin="-0.167,-0.458" Width="72" Height="25"/>
        <TextBox x:Name="fnametxt" Height="23" Margin="167,54,0,0" TextWrapping="Wrap" VerticalAlignment="Top" RenderTransformOrigin="0.501,0.452" HorizontalAlignment="Left" Width="136"/>
        <Button x:Name="exitbtn" Content="Exit" HorizontalAlignment="Left" VerticalAlignment="Top" Width="135" Margin="447,365,0,0" Height="38" RenderTransformOrigin="0.489,0.462"/>
        <Label x:Name="label" Content="Password" HorizontalAlignment="Left" Margin="92,113,0,0" VerticalAlignment="Top" RenderTransformOrigin="0.064,0.601" Height="26" Width="68"/>
        <PasswordBox x:Name="passwordBox" Margin="167,113,0,0" VerticalAlignment="Top" Height="24" HorizontalAlignment="Left" Width="136"/>
        <TextBox x:Name="stsbox" HorizontalAlignment="Left" Height="62" Margin="67,267,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="300" Background="#FFDADADA" Foreground="Black" Opacity="0.45" SelectionBrush="#FF1D6EBF" RenderTransformOrigin="0.503,-0.59" IsReadOnly="True"/>
        <TextBox x:Name="lnametxt" Height="23" Margin="167,85,0,0" TextWrapping="Wrap" VerticalAlignment="Top" HorizontalAlignment="Left" Width="136"/>
    </Grid>
</Window>
"@ 

$inputXML = $inputXML -replace 'mc:Ignorable="d"','' -replace "x:N",'N'  -replace '^<Win.*', '<Window'
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[xml]$xaml = $inputXML

#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. Double-check syntax and ensure .net is installed."}
$xaml.SelectNodes("//*[@Name]") | %{Set-Variable -Name "WPF$($_.Name)" -Value $Form.FindName($_.Name) -Scope Global}


Function global:Get-FormVariables{
if ($global:ReadmeDisplay -ne $true) {$global:ReadmeDisplay=$true}
#write-host "Found the following interactable elements from our form" -ForegroundColor Cyan
get-variable WPF*
} 


Get-FormVariables


 $WPFCreate_User.Add_Click({

$test = { for($i=1; $i -le 100000; $i++){
$z = $i + $z
$z
}}

$job = Start-Job $test 
Wait-Job $job

Receive-Job $job -OutVariable results
Remove-Job $job
$WPFstsbox.text = "$results`n"
                           }) 

 $WPFexitbtn.add_click({
 $form.Close() | out-null
 exit})

$form.ShowDialog() | Out-null

推荐答案

是的,PSJob是真正的后台"应用程序.工作.

调用Start-Job时,将启动一个单独的进程,执行您的作业/命令.

Yes, PSJobs are truly "background" jobs.

When you call Start-Job, a separate process is started, executing your job/command.

在您的脚本中,作业本身不会阻止调用线程,但是您的后续命令(Wait-Job $job)会阻止.

In your script, the job itself doesn't block the calling thread, but your subsequent command (Wait-Job $job) does.

如果仅触发Start-Job并从Click处理程序返回,则您的UI不会锁定:

If you simply fire off Start-Job and return from the Click handler, your UI wouldn't lock up:

$button.add_Click({
  $jobCode = { Start-Sleep -Seconds 10 }
  $job = Start-Job $jobCode
})

您会发现UI在不到10秒的时间内再次响应.

you'll see that the UI becomes responsive again in way lees than 10 seconds.

问题在于您不再有权访问$job,也无法控制何时显示它.

The problem is that you no longer have access to the $job and no longer control over when it is to be displayed.

要对此进行补偿,您需要后台线程或定时事件,这些事件可以定期为您检查作业并输出结果.

To compensate for this, you need background thread or a timed event that can periodically check up on the job(s) for you and output the result.

您可以为此使用Timer和PowerShell的内置事件基础结构:

You could use a Timer and PowerShell's builtin eventing infrastructure for this:

# Create timer
$backgroundTimer = New-Object System.Timers.Timer
# Set interval (ms)
$backgroundTimer.Interval = 500
# Make sure timer stops raising events after each interval
$backgroundTimer.AutoReset = $false

# Have powershell "listen" for the event in the background
Register-ObjectEvent $backgroundTimer -EventName 'Elapsed' -SourceIdentifier 'timerElapsed' -Action {

  # Loop through any completed jobs with data to return, from "oldest" to "newest"
  while(($finishedJob = Get-Job |Where-Object {$_.State -eq 'Completed' -and $_.HasMoreData}|Select-Object -First 1))
  {
    # Update the text box on your WPF form with the results
    # NOTE: the event is executed in a separate scope, thus the "global:" scope prefix
    $global:WPFstsbox.Text = "$(Receive-Job $finishedJob)`n"

    # Clean up the job
    Remove-Job $finishedJob
  }

  # Restart timer
  $backgroundTimer.Start()
}

# Enable the timer
$backgroundTimer.Enabled = $true

这篇关于乔布斯真的在Powershell的后台工作吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆