六月婷婷综合激情-六月婷婷综合-六月婷婷在线观看-六月婷婷在线-亚洲黄色在线网站-亚洲黄色在线观看网站

明輝手游網中心:是一個免費提供流行視頻軟件教程、在線學習分享的學習平臺!

在 Visual Basic .NET 中完成后臺進程(二)

[摘要]架構設計要實現我們討論的行為,顯然需要實現 Controller 類。為了使此架構能夠在多數方案中應用,我們還會定義一些正式接口,可以由 Controller 在與 UI(或客戶端)和輔助線程交互時使用。通過為客戶端和輔助線程定義正式接口,我們可以在不同的情況下使用相同的 Controller 對...

架構設計

要實現我們討論的行為,顯然需要實現 Controller 類。為了使此架構能夠在多數方案中應用,我們還會定義一些正式接口,可以由 Controller 在與 UI(或客戶端)和輔助線程交互時使用。

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

下面的 UML 類圖表顯示了 Controller 類以及 IClientIWorker 接口。它還顯示了 IController 接口,輔助代碼將通過它與 Controller 對象交互。

在 Visual Basic .NET 中實現后臺進程(二)

圖 5:Controller 和相關接口的類圖表

IClient 接口定義的方法將由 Controller 對象調用,用于向客戶端 UI 通報 Worker 的開始時間、結束時間和任何中間狀態消息。它還包含一個指示輔助代碼失敗的方法。

多數情況下,我們可以將這些方法作為由 Controller 對象發出而由 UI 處理的事件來實現。但是,從輔助線程發出事件然后由 UI 線程正確處理并非易事,因而我們將其作為一組方法來進行實現。

使控制器代碼(在輔助代碼上運行)調用 UI 中的這些方法并由 UI 線程進行處理,這樣相對要簡單得多。

同樣,IWorker 接口定義了由 Controller 對象調用的、使其可以與輔助代碼交互的方法。使用 Initialize 方法可以為輔助代碼提供對 Controller 對象的引用,而使用 Start 方法可以啟動后臺線程上的操作。

由于線程的工作方式,Start 方法無法包含任何參數。啟動新線程時,必須將不接受任何參數的方法的地址傳遞給線程。

請注意,IWorker 接口中不存在 CancelStop 方法。我們不能強制輔助代碼停止,同時也沒有這個必要;但是輔助代碼可以使用 IController 接口詢問 Controller 對象是否存在取消請求。

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

最后我們定義了 Controller 對象。該對象中包含一些可以被 UI 代碼調用的方法。其中包括 Start 方法,該方法可以通過為 Controller 對象提供對 Worker 對象的引用來啟動后臺操作。還包括 Cancel 方法,該方法用于請求取消操作。UI 也可以檢查 Running 屬性,查看是否存在取消請求;還可以檢查 Percent 屬性,查看任務完成的百分比。

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

為了實現一系列動畫點來顯示線程的活動,我們將創建一個簡單 Windows 窗體控件,該控件使用計時器以更改一系列 PictureBox 控件中的顏色。

實現方案

我們將在 Class Library(類庫)項目中實現此架構,使其可用于需要運行后臺進程的應用程序。

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

在 Visual Basic .NET 中實現后臺進程(二)

圖 6:使用項目屬性添加項目范圍內的命名空間 Imports

此操作完成后,就可以開始編碼了。讓我們先從創建接口開始。

定義接口

在名為 IClient 的項目中添加一個類,并用以下代碼替換其代碼:

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 的類,并用以下代碼替換其代碼:

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

最后添加名為 IController 的類,代碼如下:

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

至此,我們已定義了本文前面所述的所有類圖表中的接口�,F在可以實現 Controller 類了。

Controller 類

現在,我們可以實現架構的核心,Controller 類。此類中包含的代碼可用于啟動輔助線程,以及在輔助線程完成之前充當 UI 線程和輔助線程之間的媒介。

在名為 Controller 的項目中添加一個新類。首先添加 Imports,并聲明一些變量:

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

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

委托的用途很廣。在我們的示例中,委托非常重要,因為委托使我們可以讓一個線程調用窗體上的方法,使其在該窗體的 UI 線程上運行。正如 IClient 所定義的那樣,要在窗體上調用的三個方法都需要委托:

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

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

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

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

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

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

要創建線程,需要在 Worker 對象的 IWorker 接口上傳遞 Start 方法的地址。然后,只需調用線程對象的 Start 方法即可開始操作。此時我們要特別注意,UI 不應直接與 Worker 交互,Worker 也不應直接與 UI 交互。

請注意,Cancel 方法只設置一個標志,表明我們不希望繼續運行。輔助代碼應定期查看此標志,以確定是否應該停止運行。

現在,我們可以實現 Worker 對象運行時將由輔助線程調用的代碼。此代碼比較有趣,因為它必須將 DisplayCompleted 從輔助線程中轉至 UI 線程,同時還要在 UI 線程上完成此操作。

要完成此操作,我們可以使用 Form 對象的 Invoke 方法。此方法接受窗體應該調用的方法的委托指針,以及包含該方法的參數的 Object 類型數組。

Invoke 方法不直接調用窗體上的方法,而是請求窗體返回并使用窗體的 UI 線程調用該方法。此操作可通過向窗體發送 Windows 消息在后臺完成。這說明窗體獲得這些方法調用的方式與從操作系統中獲得 clickkeypress 事件的方式基本相同。

通常,這些細節不會影響大局。結果由 Invoke 方法觸發一個進程,通過該進程窗體將終止其 UI 線程上運行的方法,這就是我們要實現的目標。

再次重申,此代碼位于 Region 內,目的是為了明確它將在輔助線程上調用:

#Region " 從輔助線程調用的代碼 "  ' 從輔助線程調用,以更新顯示  ' 這將觸發對包含狀態文本的 UI 的  ' 方法調用 - 該調用是在 UI 線程上  ' 進行的  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}    ' 調用 UI 線程上的客戶端窗體    ' 以更新顯示    mClient.BeginInvoke(disp, ar)  End Sub  ' 從輔助線程調用,以表明出現故障  ' 這將觸發對包含異常對象的 UI 的  ' 方法調用 - 該調用是在 UI 線程上  ' 進行的  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 線程上調用客戶端窗體    ' 以表明出現故障    mClient.Invoke(disp, ar)  End Sub  ' 從輔助線程上調用,以指出完成的百分比  ' 值將轉到 Controller,由 UI 在需要時讀取  Private Sub SetPercent(ByVal Percent As Integer) _      Implements IController.SetPercent    mPercent = Percent  End Sub  ' 從輔助線程調用,以表明已完成  ' 我們還傳遞參數,以表明是否真正完成,  ' 以及是否取消在 UI 線程上進行的對 UI   ' 的調用  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}    ' 調用 UI 線程上的客戶端窗體    ' 以表明已完成    mClient.Invoke(comp, ar)  End Sub  ' 表明是否仍在運行或是否已請求取消  ' 這將在輔助線程上進行調用,因此  ' 輔助代碼可以查看它是否應該正常  ' 退出  Private ReadOnly Property Running() As Boolean _      Implements IController.Running    Get      Return mRunning    End Get  End Property#End Region

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

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

首先創建一個委托,從 IClient 接口指向客戶端窗體的 Failed 方法。然后聲明包含向方法傳遞參數值的 Object 類型數組。最后調用客戶端窗體的 Invoke 方法,將委托指針和參數數組傳遞給窗體。

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

整個進程是同步進行的,即對窗體進行調用時輔助線程將停止。盡管可以在顯示錯誤消息或完成消息時停止輔助線程,但我們并不希望顯示每個小狀態時都停止輔助線程。

為了避免顯示狀態時停止輔助線程,Display 方法將使用 BeginInvoke,而不使用 Invoke。BeginInvoke 使窗體上的方法調用異步進行,這樣輔助線程可以一直保持運行狀態,不需要等待窗體上的顯示方法完成:

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

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

ActivityBar 控件

最后,我們來創建顯示動畫點的 ActivityBar 控件。

在名為 ActivityBar 的項目中添加一個用戶控件。

將該控件的寬度調整為約 110,高度調整為約 20�?梢酝ㄟ^拖動邊界進行調整,也可以通過在 Properties(屬性)窗口中設置 Size 屬性進行調整。

其余的操作將通過代碼完成。要創建一系列在顯示時不停閃爍的動畫“燈”,可以使用帶有 Timer 控件的一系列 PictureBox 控件。每次 Timer 控件關閉時,我們將使下一個 PictureBox 呈綠色顯示,并將已經呈綠色顯示的 PictureBox 更改為窗體的背景色。

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

順便說一句,Components(組件)選項卡中有一個不同的 Timer 控件。它是一個多線程計時器。也就是說,該計時器將在后臺線程中引發 Elapsed 事件,而不是象 Windows 窗體計時器那樣在 UI 線程上引發 Elapsed 事件。建立 UI 時這種方法通常會產生相反的效果,因為 Elapsed 事件中的代碼顯然不能直接與我們的 UI 進行交互。

現在,在控件中添加以下代碼:

  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 事件創建 PictureBox 控件并將它們放入數組,這樣便于我們在它們之間循環。Timer 控件的 Tick 事件循環顯示,使各個控件依次呈綠色。

所有操作由 Start 方法開始,由 Stop 事件結束。由于 Stop 是一個保留字,因此把這個方法名放在方括號內:[Stop]。Stop 方法不僅可以停止計時器,還可以灰顯所有框,告訴用戶這些框中當前沒有活動。

創建 Worker 類

本文前面已簡單介紹了 Worker 類。因為我們已經定義了 IWorker 接口,所以可以增強該類,以利用我們創建的 Controller。

首先創建 Background.dll 文件。此步驟很重要,因為如果不完成此步驟,ActivityBar 控件將無法在我們建立測試窗體時顯示在工具箱上。

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

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

現在,在名為 Worker 的項目中添加一個類。其中部分代碼與前面所述的代碼相同,但還包含一些不同的代碼,用以實現 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 調用,以便獲取  ' 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          ' 它們請求取消          mController.Completed(True)          Exit Sub        End If        For innerIndex = 0 To mInner          ' 此處進行一些有意思的計算          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

 我們添加了能夠實現 IWorker.InitializeInit 方法。Controller 將調用此方法,因此以后我們可以引用 Controller 對象。

 我們還將 Work 方法更改為 Private,只是為了實現 IWorker.Start 方法。此方法將在輔助線程上運行。

 我們增強了 Work 方法,使其可以使用 Try..Catch 塊。這樣我們可以使用 Controller 上的 Failed 方法捕捉任何錯誤并將其返回給 UI。

 假設代碼正在運行,我們調用 Controller 對象的 DisplaySetPercent 方法,使它們隨著代碼的運行更新其狀態和完成的百分比。

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





主站蜘蛛池模板: 性欧美大战久久久久久久久 | 一二三四在线观看高清在线观看一 | 亚洲第一免费网站 | 亚洲国产高清精品线久久 | 青青青视频免费线看 视频 青青青视频免费观看 | 青青草原免费在线观看视频 | 亚洲国产系列久久精品99人人 | 天天干在线影院 | 伊人久久中文字幕久久cm | 中文字幕第38页永久乱码 | 日本成人在线免费 | 色综合久久综合欧美综合 | 日日噜噜夜夜狠狠久久丁香 | 色综久久天天综合绕视看 | 亚洲a人| 五月天婷婷丁香中文在线观看 | 亚洲欧美日本韩国综合在线观看 | 午夜视频www | 日韩欧美一区二区三区久久 | 在线观看91香蕉国产免费 | 青草视频网站在线观看 | 亚洲综合第一区 | 伊人狠狠色丁香婷婷综合下载 | 婷婷色在线观看 | 青青青青青青草 | 青青青久草 | 亚洲国产天堂久久综合226 | 日本中文在线视频 | 午夜成人在线视频 | 日韩视频免费在线 | 色综色天天综合网 | 色啦啦影院| 日韩欧美在线播放 | 一级毛片视频 | 青青草原在线播放 | 亚洲成人www| 香蕉视频在线观看网址 | 欧美亚洲啪啪 | 欧美专区一区二区三区 | 四虎性| 色优久久 |