我怎样才能保持UI响应,并与最新进展,而所有的工作都是在后台完成任务的本地计算机上?(How can

2019-10-29 11:35发布

我有一个显示一个程序UI ,允许用户挑选通过查询获得的虚拟机名称Xen池主服务器,然后创建针对所选择的虚拟机快照。 我想在后台创建快照,所以我可以保持UI响应和更新UI与进步创建的每个快照。

本来,我连接到Xen池主服务器,然后再执行Xen创建快照cmdlet的每个选择VM一旦UI线程。 因此, UI变得反应迟钝。

接下来,我连接到Xen池主服务器,然后做了一个start-jobbackground job )每一次VM创建虚拟机的快照。 这失败,因为Xen在创建的会话UI线程不能传递给background job (会话变量的内容,使之成块,但Xen的Connect块中cmdlet返回一个Could not find open sessions to any XenServers错误)。

然后,我搬到连接到Xen池主服务器到background job 。 这减缓操作,因为建立连接需要几秒钟,并正在为每台虚拟机做一次。 然而, UI仍然敏感,我能够用作业完成数据更新UI

我怎样才能保持UI响应,更新UI与进步创建的每个快照,而不是被强迫连接到服务器的每一次快照?

Answer 1:

更新

该解决方案确实允许作为后台作业运行将在UI线程得到更新,但它并没有保持UI响应。 见我下面Rohin响应。

解决的办法是整个循环移动到background job ,并使用Write-Progress cmdlet来对报告进度UI 。 信用卡使用Write-Progress在这种情况下cmdlet的去瑞安

这里是表示所有需要的块一个简单的演示

cls

## Job code
$ScriptBlockCode = 
{
    param($items)
    $retObj = @{}

    try
    {
        $error.clear()

        for ($i = 0; $i -lt $items.Count; $i++)
        {

            #wait to simulate the time it takes to do work on item $i
            start-sleep -Seconds (get-random -Minimum 2 -Maximum 6)

            # Use Write-Progress to report the completion of each item for as long as the job runs (Can be used to updage the UI)
            # Use the -Completed argument to suppress displaying the progress bar 
            Write-Progress  -Id ($i+1) -Activity ("For UI Update: " + $items[$i] + " processing complete") -Status "Reporting" -PercentComplete ((($i+1)/$items.Count)*100) -Completed

            #Use a hashtable to report the status of each job. To be used in the calling code after the job status is no longer Running
            #simulate some items passing, some failing
            if ((get-random -Minimum 0 -Maximum 2) -eq 0)
            {
                $itemRet = [PSCustomObject]@{status="FAIL";details="some error description"}
            }
            else
            {
                $itemRet = [PSCustomObject]@{status="PASS";details=""}
            }

            $retObj.Add($items[$i],$itemRet)

        }

        return $retObj
    }
    catch
    {
        $itemRet = [PSCustomObject]@{status="ERROR";details=$error}
        $retObj.Add("FATAL",$itemRet)
        return $retObj
    }

}

cls

#clean up before starting
Get-Job -Name "UniqueJobName" -ErrorAction SilentlyContinue | Stop-Job
Get-Job -Name "UniqueJobName" -ErrorAction SilentlyContinue | Remove-Job

#simulate 5 pieces of work
$items = @("Item A", "Item B", "Item C", "Item D", "Item E")

$job = Start-Job -Name "UniqueJobName" -ScriptBlock $ScriptBlockCode -ArgumentList ($items)

#loop and update UI until job is done

$lastActivityId = -99

While ($job.State -eq "Running")
{
    $child = $job.ChildJobs[0]

    #update the UI only if progress has started and the ActivityId has not already been reported on and the Progress report is one I care about
    if ($child.Progress.Count -gt 0 -and $child.Progress[$child.Progress.Count - 1].ActivityId -ne $lastActivityId -and ($child.Progress[$child.Progress.Count - 1]).StatusDescription -eq "Reporting")
    {
        write-host "=============================="
        write-host "in progress updates"
        write-host "=============================="
        #use the progress properties, i.e., RecordType and PercentComplete to update the UI
        $child.Progress[$child.Progress.Count - 1]

        #store this Id so we can ignore progress until Id changes
        $lastActivityId = $child.Progress[$child.Progress.Count - 1].ActivityId
    }

    #period at which the UI is updated
    start-sleep -milliseconds 250
}

$retObj = Receive-Job -Name "UniqueJobName"

write-host "=============================="
write-host "receive job"
write-host "=============================="

# Because the job may finish before progress is captured 
# for each item, use the returned values to update the UI one last time
foreach ($key in $retObj.GetEnumerator())
{
    "retObj=" + $key.name + " " + $key.Value.status + " " + $key.Value.details
}

#cleanup
Get-Job -Name "UniqueJobName" | Stop-Job
Get-Job -Name "UniqueJobName" | Remove-Job

示例输出

==============================
in progress updates
==============================


ActivityId        : 1
ParentActivityId  : -1
Activity          : For UI Update: Item A processing complete
StatusDescription : Reporting
CurrentOperation  : 
PercentComplete   : 20
SecondsRemaining  : -1
RecordType        : Completed

==============================
in progress updates
==============================
ActivityId        : 2
ParentActivityId  : -1
Activity          : For UI Update: Item B processing complete
StatusDescription : Reporting
CurrentOperation  : 
PercentComplete   : 40
SecondsRemaining  : -1
RecordType        : Completed

==============================
in progress updates
==============================
ActivityId        : 3
ParentActivityId  : -1
Activity          : For UI Update: Item C processing complete
StatusDescription : Reporting
CurrentOperation  : 
PercentComplete   : 60
SecondsRemaining  : -1
RecordType        : Completed

==============================
in progress updates
==============================
ActivityId        : 4
ParentActivityId  : -1
Activity          : For UI Update: Item D processing complete
StatusDescription : Reporting
CurrentOperation  : 
PercentComplete   : 80
SecondsRemaining  : -1
RecordType        : Completed

==============================
receive job
==============================
retObj=Item D PASS 
retObj=Item E PASS 
retObj=Item A FAIL some error description
retObj=Item B FAIL some error description
retObj=Item C PASS 


文章来源: How can I keep the UI responsive and updated with progress while all the work is done in a background job on the local computer?