明輝手游網(wǎng)中心:是一個(gè)免費(fèi)提供流行視頻軟件教程、在線學(xué)習(xí)分享的學(xué)習(xí)平臺(tái)!

在 Visual Basic .NET 中完成后臺(tái)進(jìn)程(二)

[摘要]架構(gòu)設(shè)計(jì)要實(shí)現(xiàn)我們討論的行為,顯然需要實(shí)現(xiàn) Controller 類(lèi)。為了使此架構(gòu)能夠在多數(shù)方案中應(yīng)用,我們還會(huì)定義一些正式接口,可以由 Controller 在與 UI(或客戶端)和輔助線程交互時(shí)使用。通過(guò)為客戶端和輔助線程定義正式接口,我們可以在不同的情況下使用相同的 Controller 對(duì)...

架構(gòu)設(shè)計(jì)

要實(shí)現(xiàn)我們討論的行為,顯然需要實(shí)現(xiàn) Controller 類(lèi)。為了使此架構(gòu)能夠在多數(shù)方案中應(yīng)用,我們還會(huì)定義一些正式接口,可以由 Controller 在與 UI(或客戶端)和輔助線程交互時(shí)使用。

通過(guò)為客戶端和輔助線程定義正式接口,我們可以在不同的情況下使用相同的 Controller 對(duì)象,還可以根據(jù)需要使用不同的 UI 要求和不同的 Worker 對(duì)象。

下面的 UML 類(lèi)圖表顯示了 Controller 類(lèi)以及 IClientIWorker 接口。它還顯示了 IController 接口,輔助代碼將通過(guò)它與 Controller 對(duì)象交互。

在 Visual Basic .NET 中實(shí)現(xiàn)后臺(tái)進(jìn)程(二)

圖 5:Controller 和相關(guān)接口的類(lèi)圖表

IClient 接口定義的方法將由 Controller 對(duì)象調(diào)用,用于向客戶端 UI 通報(bào) Worker 的開(kāi)始時(shí)間、結(jié)束時(shí)間和任何中間狀態(tài)消息。它還包含一個(gè)指示輔助代碼失敗的方法。

多數(shù)情況下,我們可以將這些方法作為由 Controller 對(duì)象發(fā)出而由 UI 處理的事件來(lái)實(shí)現(xiàn)。但是,從輔助線程發(fā)出事件然后由 UI 線程正確處理并非易事,因而我們將其作為一組方法來(lái)進(jìn)行實(shí)現(xiàn)。

使控制器代碼(在輔助代碼上運(yùn)行)調(diào)用 UI 中的這些方法并由 UI 線程進(jìn)行處理,這樣相對(duì)要簡(jiǎn)單得多。

同樣,IWorker 接口定義了由 Controller 對(duì)象調(diào)用的、使其可以與輔助代碼交互的方法。使用 Initialize 方法可以為輔助代碼提供對(duì) Controller 對(duì)象的引用,而使用 Start 方法可以啟動(dòng)后臺(tái)線程上的操作。

由于線程的工作方式,Start 方法無(wú)法包含任何參數(shù)。啟動(dòng)新線程時(shí),必須將不接受任何參數(shù)的方法的地址傳遞給線程。

請(qǐng)注意,IWorker 接口中不存在 CancelStop 方法。我們不能強(qiáng)制輔助代碼停止,同時(shí)也沒(méi)有這個(gè)必要;但是輔助代碼可以使用 IController 接口詢問(wèn) Controller 對(duì)象是否存在取消請(qǐng)求。

IController 接口定義了輔助代碼可以在 Controller 對(duì)象上調(diào)用的方法。它允許輔助代碼檢查 Running 標(biāo)志。如果存在取消請(qǐng)求,Running 標(biāo)志即為 False。它還允許輔助代碼在工作完成或無(wú)法完成時(shí)告訴 Controller,并允許使用狀態(tài)消息和完成百分比值(0 到 100 之間的 Integer)更新 Controller。

最后我們定義了 Controller 對(duì)象。該對(duì)象中包含一些可以被 UI 代碼調(diào)用的方法。其中包括 Start 方法,該方法可以通過(guò)為 Controller 對(duì)象提供對(duì) Worker 對(duì)象的引用來(lái)啟動(dòng)后臺(tái)操作。還包括 Cancel 方法,該方法用于請(qǐng)求取消操作。UI 也可以檢查 Running 屬性,查看是否存在取消請(qǐng)求;還可以檢查 Percent 屬性,查看任務(wù)完成的百分比。

Controller 類(lèi)中包含的 constructor 方法接受 IClient 作為參數(shù),還允許 UI 為 Controller 提供對(duì)窗體(用于處理 Worker 中的顯示消息)的引用。

為了實(shí)現(xiàn)一系列動(dòng)畫(huà)點(diǎn)來(lái)顯示線程的活動(dòng),我們將創(chuàng)建一個(gè)簡(jiǎn)單 Windows 窗體控件,該控件使用計(jì)時(shí)器以更改一系列 PictureBox 控件中的顏色。

實(shí)現(xiàn)方案

我們將在 Class Library(類(lèi)庫(kù))項(xiàng)目中實(shí)現(xiàn)此架構(gòu),使其可用于需要運(yùn)行后臺(tái)進(jìn)程的應(yīng)用程序。

打開(kāi) Visual Studio .NET,然后創(chuàng)建一個(gè)名為 Background 的新 Class Library(類(lèi)庫(kù))應(yīng)用程序。由于此庫(kù)將包含 Windows 窗體控件和窗體,因此需要使用 Add References(添加引用)對(duì)話框引用 System.Windows.Forms.dllSystem.Windows.Drawing.dll。此外,我們可以使用項(xiàng)目的屬性對(duì)話框在這些項(xiàng)目范圍內(nèi)導(dǎo)入命名空間,如圖 6 所示。

在 Visual Basic .NET 中實(shí)現(xiàn)后臺(tái)進(jìn)程(二)

圖 6:使用項(xiàng)目屬性添加項(xiàng)目范圍內(nèi)的命名空間 Imports

此操作完成后,就可以開(kāi)始編碼了。讓我們先從創(chuàng)建接口開(kāi)始。

定義接口

在名為 IClient 的項(xiàng)目中添加一個(gè)類(lèi),并用以下代碼替換其代碼:

Public Interface IClient  Sub Start(ByVal Controller As Controller)  Sub Display(ByVal Text As String)  Sub Failed(ByVal e As Exception)  Sub Completed(ByVal Cancelled As Boolean)End Interface

然后添加名為 IWorker 的類(lèi),并用以下代碼替換其代碼:

Public Interface IWorker  Sub Initialize(ByVal Controller As IController)  Sub Start()End Interface

最后添加名為 IController 的類(lèi),代碼如下:

Public Interface IController  ReadOnly Property Running() As Boolean  Sub Display(ByVal Text As String)  Sub SetPercent(ByVal Percent As Integer)  Sub Failed(ByVal e As Exception)  Sub Completed(ByVal Cancelled As Boolean)End Interface

至此,我們已定義了本文前面所述的所有類(lèi)圖表中的接口。現(xiàn)在可以實(shí)現(xiàn) Controller 類(lèi)了。

Controller 類(lèi)

現(xiàn)在,我們可以實(shí)現(xiàn)架構(gòu)的核心,Controller 類(lèi)。此類(lèi)中包含的代碼可用于啟動(dòng)輔助線程,以及在輔助線程完成之前充當(dāng) UI 線程和輔助線程之間的媒介。

在名為 Controller 的項(xiàng)目中添加一個(gè)新類(lèi)。首先添加 Imports,并聲明一些變量:

Imports System.ThreadingPublic Class Controller  Implements IController  Private mWorker As IWorker  Private mClient As Form  Private mRunning As Boolean  Private mPercent As Integer

然后需要聲明一些委托。委托是方法的正式指針,而且方法的委托必須具有與方法本身相同的方法簽名(參數(shù)類(lèi)型等)。

委托的用途很廣。在我們的示例中,委托非常重要,因?yàn)槲惺刮覀兛梢宰屢粋(gè)線程調(diào)用窗體上的方法,使其在該窗體的 UI 線程上運(yùn)行。正如 IClient 所定義的那樣,要在窗體上調(diào)用的三個(gè)方法都需要委托:

  ' 此委托簽名與 IClient.Completed   ' 中的簽名相匹配,并用于安全地  ' 調(diào)用 UI 線程上的方法  Private Delegate Sub CompletedDelegate(ByVal Cancelled As Boolean)  ' 此委托簽名與 IClient.Display   ' 中的簽名相匹配,并用于安全地  ' 調(diào)用 UI 線程上的方法  Private Delegate Sub DisplayDelegate(ByVal Text As String)  ' 此委托簽名與 IClient.Failed   ' 中的簽名相匹配,并用于安全地  ' 調(diào)用 UI 線程上的方法  Private Delegate Sub FailedDelegate(ByVal e As Exception) 

IClient 還定義了 Start 方法,但是該方法可以從 UI 線程調(diào)用,因此不需要委托。

下面編寫(xiě)將從 UI 線程調(diào)用的代碼。代碼中包括 constructor 方法、StartCancel 方法以及 Percent 屬性。我將這些內(nèi)容放入 Region 中,便于大家清楚地了解它們是從 UI 線程調(diào)用的。

#Region " 從 UI 線程調(diào)用的代碼 "  ' 使用客戶端初始化 Controller  Public Sub New(ByVal Client As IClient)    mClient = CType(Client, Form)  End Sub  ' 此方法由 UI 調(diào)用,因此在   ' UI 線程上運(yùn)行。此處我們將  ' 啟動(dòng)輔助線程  Public Sub Start(Optional ByVal Worker As IWorker = Nothing)    ' 如果輔助線程已經(jīng)啟動(dòng),將產(chǎn)生錯(cuò)誤    If mRunning Then      Throw New Exception("Background process already running")    End If    mRunning = True    ' 存儲(chǔ)對(duì)輔助對(duì)象的引用,并    ' 初始化輔助對(duì)象,使其包含    ' 對(duì) Controller 的引用    mWorker = Worker    mWorker.Initialize(Me)    ' 創(chuàng)建后臺(tái)線程    ' 以進(jìn)行后臺(tái)操作    Dim backThread As New Thread(AddressOf mWorker.Start)    ' 開(kāi)始后臺(tái)工作    backThread.Start()    ' 告訴客戶端后臺(tái)工作已開(kāi)始    CType(mClient, IClient).Start(Me)  End Sub  ' 此代碼由 UI 調(diào)用,因此在 UI   ' 線程上運(yùn)行。它只設(shè)置了請(qǐng)求  ' 取消的標(biāo)志  Public Sub Cancel()    mRunning = False  End Sub  ' 返回完成百分比值,并且  ' 只被 UI 線程調(diào)用  Public ReadOnly Property Percent() As Integer    Get      Return mPercent    End Get  End Property#End Region

此處唯一比較特殊的代碼位于 Start 方法中,我們可以在該方法中創(chuàng)建輔助線程然后啟動(dòng)該線程:

    Dim backThread As New Thread(AddressOf mWorker.Start)    backThread.Start()

要?jiǎng)?chuàng)建線程,需要在 Worker 對(duì)象的 IWorker 接口上傳遞 Start 方法的地址。然后,只需調(diào)用線程對(duì)象的 Start 方法即可開(kāi)始操作。此時(shí)我們要特別注意,UI 不應(yīng)直接與 Worker 交互,Worker 也不應(yīng)直接與 UI 交互。

請(qǐng)注意,Cancel 方法只設(shè)置一個(gè)標(biāo)志,表明我們不希望繼續(xù)運(yùn)行。輔助代碼應(yīng)定期查看此標(biāo)志,以確定是否應(yīng)該停止運(yùn)行。

現(xiàn)在,我們可以實(shí)現(xiàn) Worker 對(duì)象運(yùn)行時(shí)將由輔助線程調(diào)用的代碼。此代碼比較有趣,因?yàn)樗仨殞?DisplayCompleted 從輔助線程中轉(zhuǎn)至 UI 線程,同時(shí)還要在 UI 線程上完成此操作。

要完成此操作,我們可以使用 Form 對(duì)象的 Invoke 方法。此方法接受窗體應(yīng)該調(diào)用的方法的委托指針,以及包含該方法的參數(shù)的 Object 類(lèi)型數(shù)組。

Invoke 方法不直接調(diào)用窗體上的方法,而是請(qǐng)求窗體返回并使用窗體的 UI 線程調(diào)用該方法。此操作可通過(guò)向窗體發(fā)送 Windows 消息在后臺(tái)完成。這說(shuō)明窗體獲得這些方法調(diào)用的方式與從操作系統(tǒng)中獲得 clickkeypress 事件的方式基本相同。

通常,這些細(xì)節(jié)不會(huì)影響大局。結(jié)果由 Invoke 方法觸發(fā)一個(gè)進(jìn)程,通過(guò)該進(jìn)程窗體將終止其 UI 線程上運(yùn)行的方法,這就是我們要實(shí)現(xiàn)的目標(biāo)。

再次重申,此代碼位于 Region 內(nèi),目的是為了明確它將在輔助線程上調(diào)用:

#Region " 從輔助線程調(diào)用的代碼 "  ' 從輔助線程調(diào)用,以更新顯示  ' 這將觸發(fā)對(duì)包含狀態(tài)文本的 UI 的  ' 方法調(diào)用 - 該調(diào)用是在 UI 線程上  ' 進(jìn)行的  Private Sub Display(ByVal Text As String) _      Implements IController.Display    Dim disp As New DisplayDelegate( _      AddressOf CType(mClient, IClient).Display)    Dim ar() As Object = {Text}    ' 調(diào)用 UI 線程上的客戶端窗體    ' 以更新顯示    mClient.BeginInvoke(disp, ar)  End Sub  ' 從輔助線程調(diào)用,以表明出現(xiàn)故障  ' 這將觸發(fā)對(duì)包含異常對(duì)象的 UI 的  ' 方法調(diào)用 - 該調(diào)用是在 UI 線程上  ' 進(jìn)行的  Private Sub Failed(ByVal e As Exception) _      Implements IController.Failed    Dim disp As New FailedDelegate(_      AddressOf CType(mClient, IClient).Failed)    Dim ar() As Object = {e}    ' 在 UI 線程上調(diào)用客戶端窗體    ' 以表明出現(xiàn)故障    mClient.Invoke(disp, ar)  End Sub  ' 從輔助線程上調(diào)用,以指出完成的百分比  ' 值將轉(zhuǎn)到 Controller,由 UI 在需要時(shí)讀取  Private Sub SetPercent(ByVal Percent As Integer) _      Implements IController.SetPercent    mPercent = Percent  End Sub  ' 從輔助線程調(diào)用,以表明已完成  ' 我們還傳遞參數(shù),以表明是否真正完成,  ' 以及是否取消在 UI 線程上進(jìn)行的對(duì) UI   ' 的調(diào)用  Private Sub Completed(ByVal Cancelled As Boolean) _      Implements IController.Completed    mRunning = False    Dim comp As New CompletedDelegate( _      AddressOf CType(mClient, IClient).Completed)    Dim ar() As Object = {Cancelled}    ' 調(diào)用 UI 線程上的客戶端窗體    ' 以表明已完成    mClient.Invoke(comp, ar)  End Sub  ' 表明是否仍在運(yùn)行或是否已請(qǐng)求取消  ' 這將在輔助線程上進(jìn)行調(diào)用,因此  ' 輔助代碼可以查看它是否應(yīng)該正常  ' 退出  Private ReadOnly Property Running() As Boolean _      Implements IController.Running    Get      Return mRunning    End Get  End Property#End Region

FailedCompleted 方法利用窗體的 Invoke 方法。例如,Failed 方法可以執(zhí)行以下操作:

    Dim disp As New FailedDelegate(_      AddressOf CType(mClient, IClient).Failed)    Dim ar() As Object = {e}    ' 調(diào)用 UI 線程上的客戶端窗體    ' 以表明出現(xiàn)故障    mClient.Invoke(disp, ar)

首先創(chuàng)建一個(gè)委托,從 IClient 接口指向客戶端窗體的 Failed 方法。然后聲明包含向方法傳遞參數(shù)值的 Object 類(lèi)型數(shù)組。最后調(diào)用客戶端窗體的 Invoke 方法,將委托指針和參數(shù)數(shù)組傳遞給窗體。

窗體將在 UI 線程(窗體在這里可以安全運(yùn)行以更新顯示)上使用這些參數(shù)調(diào)用此方法。

整個(gè)進(jìn)程是同步進(jìn)行的,即對(duì)窗體進(jìn)行調(diào)用時(shí)輔助線程將停止。盡管可以在顯示錯(cuò)誤消息或完成消息時(shí)停止輔助線程,但我們并不希望顯示每個(gè)小狀態(tài)時(shí)都停止輔助線程。

為了避免顯示狀態(tài)時(shí)停止輔助線程,Display 方法將使用 BeginInvoke,而不使用 InvokeBeginInvoke 使窗體上的方法調(diào)用異步進(jìn)行,這樣輔助線程可以一直保持運(yùn)行狀態(tài),不需要等待窗體上的顯示方法完成:

    Dim disp As New DisplayDelegate( _      AddressOf CType(mClient, IClient).Display)    Dim ar() As Object = {Text}    ' 調(diào)用 UI 線程上的客戶端窗體    ' 以更新顯示    mClient.BeginInvoke(disp, ar)

以這種方式使用 BeginInvoke 可以防止輔助線程停止,使輔助線程具有盡可能高的性能。

ActivityBar 控件

最后,我們來(lái)創(chuàng)建顯示動(dòng)畫(huà)點(diǎn)的 ActivityBar 控件。

在名為 ActivityBar 的項(xiàng)目中添加一個(gè)用戶控件。

將該控件的寬度調(diào)整為約 110,高度調(diào)整為約 20。可以通過(guò)拖動(dòng)邊界進(jìn)行調(diào)整,也可以通過(guò)在 Properties(屬性)窗口中設(shè)置 Size 屬性進(jìn)行調(diào)整。

其余的操作將通過(guò)代碼完成。要?jiǎng)?chuàng)建一系列在顯示時(shí)不停閃爍的動(dòng)畫(huà)“燈”,可以使用帶有 Timer 控件的一系列 PictureBox 控件。每次 Timer 控件關(guān)閉時(shí),我們將使下一個(gè) PictureBox 呈綠色顯示,并將已經(jīng)呈綠色顯示的 PictureBox 更改為窗體的背景色。

將 Windows Forms(Windows 窗體)選項(xiàng)卡中的 Timer 控件放入窗體中,然后將其名稱更改為 tmAnim。同時(shí)將 Interval 屬性設(shè)置為 300,以獲得較好的動(dòng)畫(huà)速度。

順便說(shuō)一句,Components(組件)選項(xiàng)卡中有一個(gè)不同的 Timer 控件。它是一個(gè)多線程計(jì)時(shí)器。也就是說(shuō),該計(jì)時(shí)器將在后臺(tái)線程中引發(fā) Elapsed 事件,而不是象 Windows 窗體計(jì)時(shí)器那樣在 UI 線程上引發(fā) Elapsed 事件。建立 UI 時(shí)這種方法通常會(huì)產(chǎn)生相反的效果,因?yàn)?Elapsed 事件中的代碼顯然不能直接與我們的 UI 進(jìn)行交互。

現(xiàn)在,在控件中添加以下代碼:

  Private mBoxes As New ArrayList()  Private mCount As Integer  Private Sub ActivityBar_Load(ByVal sender As System.Object, _    ByVal e As System.EventArgs) Handles MyBase.Load    Dim index As Integer    If mBoxes.Count = 0 Then      For index = 0 To 6        mBoxes.Add(CreateBox(index))      Next    End If    mCount = 0  End Sub  Private Function CreateBox(ByVal index As Integer) As PictureBox    Dim box As New PictureBox()    With box      SetPosition(box, index)      .BorderStyle = BorderStyle.Fixed3D      .Parent = Me      .Visible = True    End With    Return box  End Function  Private Sub GrayDisplay()    Dim index As Integer    For index = 0 To 6      CType(mBoxes(index), PictureBox).BackColor = Me.BackColor    Next  End Sub  Private Sub SetPosition(ByVal Box As PictureBox, ByVal Index As Integer)    Dim left As Integer = CInt(Me.Width / 2 - 7 * 14 / 2)    Dim top As Integer = CInt(Me.Height / 2 - 5)    With Box      .Height = 10      .Width = 10      .Top = top      .Left = left + Index * 14    End With  End Sub  Private Sub tmAnim_Tick(ByVal sender As System.Object, _    ByVal e As System.EventArgs) Handles tmAnim.Tick    CType(mBoxes((mCount + 1) Mod 7), PictureBox).BackColor = _      Color.LightGreen    CType(mBoxes(mCount Mod 7), PictureBox).BackColor = Me.BackColor    mCount += 1    If mCount > 6 Then mCount = 0  End Sub  Public Sub Start()    CType(mBoxes(0), PictureBox).BackColor = Color.LightGreen    tmAnim.Enabled = True  End Sub  Public Sub [Stop]()    tmAnim.Enabled = False    GrayDisplay()  End Sub  Private Sub ActivityBar_Resize(ByVal sender As Object, _    ByVal e As System.EventArgs) Handles MyBase.Resize    Dim index As Integer    For index = 0 To mBoxes.Count - 1      SetPosition(CType(mBoxes(index), PictureBox), index)    Next  End Sub

窗體的 Load 事件創(chuàng)建 PictureBox 控件并將它們放入數(shù)組,這樣便于我們?cè)谒鼈冎g循環(huán)。Timer 控件的 Tick 事件循環(huán)顯示,使各個(gè)控件依次呈綠色。

所有操作由 Start 方法開(kāi)始,由 Stop 事件結(jié)束。由于 Stop 是一個(gè)保留字,因此把這個(gè)方法名放在方括號(hào)內(nèi):[Stop]Stop 方法不僅可以停止計(jì)時(shí)器,還可以灰顯所有框,告訴用戶這些框中當(dāng)前沒(méi)有活動(dòng)。

創(chuàng)建 Worker 類(lèi)

本文前面已簡(jiǎn)單介紹了 Worker 類(lèi)。因?yàn)槲覀円呀?jīng)定義了 IWorker 接口,所以可以增強(qiáng)該類(lèi),以利用我們創(chuàng)建的 Controller。

首先創(chuàng)建 Background.dll 文件。此步驟很重要,因?yàn)槿绻煌瓿纱瞬襟E,ActivityBar 控件將無(wú)法在我們建立測(cè)試窗體時(shí)顯示在工具箱上。

在解決方案中添加名為 bgTest 的 Windows Forms Application(Windows 窗體應(yīng)用程序)。在 Solution Explorer(解決方案資源瀏覽器)中用右鍵單擊該項(xiàng)目并選擇相應(yīng)的菜單項(xiàng),將該程序設(shè)置為啟動(dòng)項(xiàng)目。

然后使用 Add References(添加引用)對(duì)話框中的 Projects(項(xiàng)目)選項(xiàng)卡,添加對(duì) Background 項(xiàng)目的引用。

現(xiàn)在,在名為 Worker 的項(xiàng)目中添加一個(gè)類(lèi)。其中部分代碼與前面所述的代碼相同,但還包含一些不同的代碼,用以實(shí)現(xiàn) IWorker 接口(此處突出顯示的部分):

Imports BackgroundPublic Class Worker  Implements IWorker  Private mController As IController  Private mInner As Integer  Private mOuter As Integer  Public Sub New(ByVal InnerSize As Integer, ByVal OuterSize As Integer)    mInner = InnerSize    mOuter = OuterSize  End Sub  ' 由 Controller 調(diào)用,以便獲取  ' Controller 的引用  Private Sub Init(ByVal Controller As IController) _      Implements IWorker.Initialize    mController = Controller  End Sub  Private Sub Work() Implements IWorker.Start    Dim innerIndex As Integer    Dim outerIndex As Integer    Dim value As Double    Try      For outerIndex = 0 To mOuter        If mController.Running Then          mController.Display("Outer loop " & outerIndex & " starting")          mController.SetPercent(CInt(outerIndex / mOuter * 100))        Else          ' 它們請(qǐng)求取消          mController.Completed(True)          Exit Sub        End If        For innerIndex = 0 To mInner          ' 此處進(jìn)行一些有意思的計(jì)算          value = Math.Sqrt(CDbl(innerIndex - outerIndex))        Next      Next      mController.SetPercent(100)      mController.Completed(False)    Catch e As Exception      mController.Failed(e)    End Try  End SubEnd Class

 我們添加了能夠?qū)崿F(xiàn) IWorker.InitializeInit 方法。Controller 將調(diào)用此方法,因此以后我們可以引用 Controller 對(duì)象。

 我們還將 Work 方法更改為 Private,只是為了實(shí)現(xiàn) IWorker.Start 方法。此方法將在輔助線程上運(yùn)行。

 我們?cè)鰪?qiáng)了 Work 方法,使其可以使用 Try..Catch 塊。這樣我們可以使用 Controller 上的 Failed 方法捕捉任何錯(cuò)誤并將其返回給 UI。

 假設(shè)代碼正在運(yùn)行,我們調(diào)用 Controller 對(duì)象的 DisplaySetPercent 方法,使它們隨著代碼的運(yùn)行更新其狀態(tài)和完成的百分比。

 我們還定期檢查 Controller 對(duì)象的 Running 屬性,查看是否存在取消請(qǐng)求。如果存在取消請(qǐng)求,則停止進(jìn)程,并指示由于取消請(qǐng)求而停止操作。