處理非同步應用程式中的重新進入 (Visual Basic)

當您將非同步程式碼納入您的應用程式時,應該考慮並防止可能發生的重新進入,也就是在完成前重新進入的非同步作業。 如果您不找出並處理重新進入的可能性,它可能會導致非預期的結果。

注意

若要執行範例,您必須在電腦上安裝 Visual Studio 2012 或更新版本以及 .NET Framework 4.5 或更新版本。

注意

傳輸層安全性 (TLS) 1.2 版現在是可在應用程式開發中使用的最低版本。 如果應用程式是以 .NET Framework 4.7 之前的版本為目標,請參閱下列文章,以取得 .NET Framework 的傳輸層安全性 (TLS) 最佳做法

辨識重新進入

在本主題的範例中,使用者選擇 [開始] 按鈕來起始非同步應用程式,該應用程式會下載一系列網站,並計算下載的位元組總數。 此範例的同步版本會回應相同的方式,不論使用者選擇按鈕的次數為何,因為在第一次之後,UI 執行緒會忽略這些事件,直到應用程式完成執行為止。 但在非同步應用程式中,UI 執行緒會繼續回應,而且您可能在它完成之前重新進入非同步作業。

如果使用者只選擇一次 [開始] 按鈕,則下列範例會顯示預期的輸出。 下載的網站清單會顯示每個網站的大小 (以位元組為單位)。 結尾會出現位元組總數。

1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
3. msdn.microsoft.com/library/jj155761.aspx                29019
4. msdn.microsoft.com/library/hh290140.aspx               117152
5. msdn.microsoft.com/library/hh524395.aspx                68959
6. msdn.microsoft.com/library/ms404677.aspx               197325
7. msdn.microsoft.com                                            42972
8. msdn.microsoft.com/library/ff730837.aspx               146159

TOTAL bytes returned:  890591

不過,如果使用者選擇按鈕多次,則會重複叫用事件處理常式,且每次都會重新進入下載程序。 如此一來,同時會執行數個非同步作業,輸出會與結果交錯,且導出令人困惑的位元組總數。

1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
3. msdn.microsoft.com/library/jj155761.aspx                29019
4. msdn.microsoft.com/library/hh290140.aspx               117152
5. msdn.microsoft.com/library/hh524395.aspx                68959
1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
6. msdn.microsoft.com/library/ms404677.aspx               197325
3. msdn.microsoft.com/library/jj155761.aspx                29019
7. msdn.microsoft.com                                            42972
4. msdn.microsoft.com/library/hh290140.aspx               117152
8. msdn.microsoft.com/library/ff730837.aspx               146159

TOTAL bytes returned:  890591

5. msdn.microsoft.com/library/hh524395.aspx                68959
1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
6. msdn.microsoft.com/library/ms404677.aspx               197325
3. msdn.microsoft.com/library/jj155761.aspx                29019
4. msdn.microsoft.com/library/hh290140.aspx               117152
7. msdn.microsoft.com                                            42972
5. msdn.microsoft.com/library/hh524395.aspx                68959
8. msdn.microsoft.com/library/ff730837.aspx               146159

TOTAL bytes returned:  890591

6. msdn.microsoft.com/library/ms404677.aspx               197325
7. msdn.microsoft.com                                            42972
8. msdn.microsoft.com/library/ff730837.aspx               146159

TOTAL bytes returned:  890591

您可以捲動到本主題的結尾來檢閱產生此輸出的程式碼。 您可以將方案下載到本機電腦,然後執行 WebsiteDownload 專案;或使用本主題結尾的程式碼來建立您自己的專案,以實驗程式碼。如需詳細資訊和指示,請參閱檢閱及執行範例應用程式

處理重新進入

您可以各種不同的方式重新進入,視您要應用程式執行的工作而定。 本主題提供下列範例:

停用 [開始] 按鈕

您可以停用 StartButton_Click 事件處理常式頂端的按鈕,以便在執行作業時封鎖 [開始] 按鈕。 作業完成時,您可以在 Finally 區塊內部重新啟用按鈕,讓使用者可再次執行應用程式。

下列程式碼會顯示這些變更 (以星號標記)。 您可以新增本主題結尾程式碼的變更,或者從非同步範例︰重新進入 .NET 桌面應用程式下載完成的應用程式。 專案名稱是 DisableStartButton。

Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)
    ' This line is commented out to make the results clearer in the output.
    'ResultsTextBox.Text = ""

    ' ***Disable the Start button until the downloads are complete.
    StartButton.IsEnabled = False

    Try
        Await AccessTheWebAsync()

    Catch ex As Exception
        ResultsTextBox.Text &= vbCrLf & "Downloads failed."
    ' ***Enable the Start button in case you want to run the program again.
    Finally
        StartButton.IsEnabled = True

    End Try
End Sub

因為變更,按鈕不會在 AccessTheWebAsync 正在下載網站時反應,因此程序將無法重新進入。

取消後再重新啟動作業

您不必停用 [開始] 按鈕,您可以讓按鈕保持作用中,但如果使用者再次選擇該按鈕,請取消已在執行的作業,並讓最近啟動的作業繼續執行。

如需取消的詳細資訊,請參閱微調非同步應用程式 (Visual Basic)

若要設定此案例,請對檢閱及執行範例應用程式中提供的基本程式碼進行下列變更。 您也可以從非同步範例︰重新進入 .NET 桌面應用程式下載完成的應用程式。 此專案的名稱是 CancelAndRestart。

  1. 宣告 CancellationTokenSource 變數 cts,這是在所有方法的範圍內。

    Class MainWindow // Or Class MainPage
    
        ' *** Declare a System.Threading.CancellationTokenSource.
        Dim cts As CancellationTokenSource
    
  2. StartButton_Click 中,判定作業是否已在進行中。 如果 cts 值為 Nothing,則沒有已在使用中的作業。 如果此值不是 Nothing,則表示已取消已在執行的作業。

    ' *** If a download process is already underway, cancel it.
    If cts IsNot Nothing Then
        cts.Cancel()
    End If
    
  3. cts 設為代表目前程序的不同值。

    ' *** Now set cts to cancel the current process if the button is chosen again.
    Dim newCTS As CancellationTokenSource = New CancellationTokenSource()
    cts = newCTS
    
  4. StartButton_Click 結束時,目前的處理序已完成,因此將 cts 值設定回 Nothing

    ' *** When the process completes, signal that another process can proceed.
    If cts Is newCTS Then
        cts = Nothing
    End If
    

下列程式碼顯示 StartButton_Click 中的所有變更。 新增的項目會以星號標記。

Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)

    ' This line is commented out to make the results clearer.
    'ResultsTextBox.Text = ""

    ' *** If a download process is underway, cancel it.
    If cts IsNot Nothing Then
        cts.Cancel()
    End If

    ' *** Now set cts to cancel the current process if the button is chosen again.
    Dim newCTS As CancellationTokenSource = New CancellationTokenSource()
    cts = newCTS

    Try
        ' *** Send a token to carry the message if the operation is canceled.
        Await AccessTheWebAsync(cts.Token)

    Catch ex As OperationCanceledException
        ResultsTextBox.Text &= vbCrLf & "Download canceled." & vbCrLf

    Catch ex As Exception
        ResultsTextBox.Text &= vbCrLf & "Downloads failed."
    End Try

    ' *** When the process is complete, signal that another process can proceed.
    If cts Is newCTS Then
        cts = Nothing
    End If
End Sub

AccessTheWebAsync 中進行下列變更。

  • 加入參數以接受來自 StartButton_Click 的取消語彙基元。

  • 使用 GetAsync 方法來下載網站,因為 GetAsync 接受 CancellationToken 引數。

  • 在呼叫 DisplayResults 顯示每個下載網站的結果前,請檢查 ct 以確認目前的作業尚未取消。

下列程式碼會顯示這些變更 (以星號標記)。

' *** Provide a parameter for the CancellationToken from StartButton_Click.
Private Async Function AccessTheWebAsync(ct As CancellationToken) As Task

    ' Declare an HttpClient object.
    Dim client = New HttpClient()

    ' Make a list of web addresses.
    Dim urlList As List(Of String) = SetUpURLList()

    Dim total = 0
    Dim position = 0

    For Each url In urlList
        ' *** Use the HttpClient.GetAsync method because it accepts a
        ' cancellation token.
        Dim response As HttpResponseMessage = Await client.GetAsync(url, ct)

        ' *** Retrieve the website contents from the HttpResponseMessage.
        Dim urlContents As Byte() = Await response.Content.ReadAsByteArrayAsync()

        ' *** Check for cancellations before displaying information about the
        ' latest site.
        ct.ThrowIfCancellationRequested()

        position += 1
        DisplayResults(url, urlContents, position)

        ' Update the total.
        total += urlContents.Length
    Next

    ' Display the total count for all of the websites.
    ResultsTextBox.Text &=
        String.Format(vbCrLf & vbCrLf & "TOTAL bytes returned:  " & total & vbCrLf)
End Function

如果在這個應用程式正在執行時選擇 [開始] 按鈕多次,則應該會產生類似下列的輸出結果:

1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
3. msdn.microsoft.com/library/jj155761.aspx                29019
4. msdn.microsoft.com/library/hh290140.aspx               122505
5. msdn.microsoft.com/library/hh524395.aspx                68959
6. msdn.microsoft.com/library/ms404677.aspx               197325
Download canceled.

1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
3. msdn.microsoft.com/library/jj155761.aspx                29019
Download canceled.

1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
3. msdn.microsoft.com/library/jj155761.aspx                29019
4. msdn.microsoft.com/library/hh290140.aspx               117152
5. msdn.microsoft.com/library/hh524395.aspx                68959
6. msdn.microsoft.com/library/ms404677.aspx               197325
7. msdn.microsoft.com                                            42972
8. msdn.microsoft.com/library/ff730837.aspx               146159

TOTAL bytes returned:  890591

若要排除部分清單,請取消註解 StartButton_Click 中程式碼的第一行,以清除每次使用者重新啟動作業時出現的文字方塊。

執行多個作業並將輸出加入佇列

此第三個範例是最複雜的,因為每當使用者選擇 [開始] 按鈕時,應用程式就會啟動另一個非同步作業,而且所有作業都會執行到完成為止。 所有要求的作業會以非同步方式從清單下載網站,但作業的輸出會以循序方式呈現。 也就是隨辨識重新進入顯示輸出,實際的下載活動會交錯進行,但每個群組的結果清單會循序呈現。

作業會共用全域 TaskpendingWork,做為顯示程序的閘道管理員。

您可以執行這個範例,方法是將變更貼至建置應用程式中的程式碼,或者遵循下載應用程式的指示來下載範例,然後執行 QueueResults 專案。

下列輸出顯示當使用者只選擇 [開始] 按鈕一次時的結果。 字母標籤 A,表示第一次選擇 [開始] 按鈕時的結果。 數字顯示下載目標清單中的 URL 順序。

#Starting group A.
#Task assigned for group A.

A-1. msdn.microsoft.com/library/hh191443.aspx                87389
A-2. msdn.microsoft.com/library/aa578028.aspx               209858
A-3. msdn.microsoft.com/library/jj155761.aspx                30870
A-4. msdn.microsoft.com/library/hh290140.aspx               119027
A-5. msdn.microsoft.com/library/hh524395.aspx                71260
A-6. msdn.microsoft.com/library/ms404677.aspx               199186
A-7. msdn.microsoft.com                                            53266
A-8. msdn.microsoft.com/library/ff730837.aspx               148020

TOTAL bytes returned:  918876

#Group A is complete.

如果使用者選擇 [開始] 按鈕三次,應用程式會產生類似下列幾行的輸出。 以井字號 (#) 開頭的資訊行會追蹤應用程式的進度。

#Starting group A.
#Task assigned for group A.

A-1. msdn.microsoft.com/library/hh191443.aspx                87389
A-2. msdn.microsoft.com/library/aa578028.aspx               207089
A-3. msdn.microsoft.com/library/jj155761.aspx                30870
A-4. msdn.microsoft.com/library/hh290140.aspx               119027
A-5. msdn.microsoft.com/library/hh524395.aspx                71259
A-6. msdn.microsoft.com/library/ms404677.aspx               199185

#Starting group B.
#Task assigned for group B.

A-7. msdn.microsoft.com                                            53266

#Starting group C.
#Task assigned for group C.

A-8. msdn.microsoft.com/library/ff730837.aspx               148010

TOTAL bytes returned:  916095

B-1. msdn.microsoft.com/library/hh191443.aspx                87389
B-2. msdn.microsoft.com/library/aa578028.aspx               207089
B-3. msdn.microsoft.com/library/jj155761.aspx                30870
B-4. msdn.microsoft.com/library/hh290140.aspx               119027
B-5. msdn.microsoft.com/library/hh524395.aspx                71260
B-6. msdn.microsoft.com/library/ms404677.aspx               199186

#Group A is complete.

B-7. msdn.microsoft.com                                            53266
B-8. msdn.microsoft.com/library/ff730837.aspx               148010

TOTAL bytes returned:  916097

C-1. msdn.microsoft.com/library/hh191443.aspx                87389
C-2. msdn.microsoft.com/library/aa578028.aspx               207089

#Group B is complete.

C-3. msdn.microsoft.com/library/jj155761.aspx                30870
C-4. msdn.microsoft.com/library/hh290140.aspx               119027
C-5. msdn.microsoft.com/library/hh524395.aspx                72765
C-6. msdn.microsoft.com/library/ms404677.aspx               199186
C-7. msdn.microsoft.com                                            56190
C-8. msdn.microsoft.com/library/ff730837.aspx               148010

TOTAL bytes returned:  920526

#Group C is complete.

群組 B 和 C 在群組 A 完成前啟動,但每個群組的輸出會單獨顯示。 先顯示群組 A 的所有輸出,接著是群組 B 的所有輸出,然後是群組 C 的所有輸出。應用程式一律依序顯示群組,且對每個群組,一律會根據 URL 在 URL 清單中的出現順序,顯示個別網站的相關資訊。

不過,您無法預測實際的下載順序。 啟動多個群組之後,它們產生的下載工作會全部啟用。 您不能假設 A-1 將在 B-1 之前下載,也不能假設 A-1 將在 A-2 之前下載。

全域定義

範例程式碼包含所有方法都會看見的下列兩個全域宣告。

Class MainWindow    ' Class MainPage in Windows Store app.

    ' ***Declare the following variables where all methods can access them.
    Private pendingWork As Task = Nothing
    Private group As Char = ChrW(AscW("A") - 1)

Task 變數 pendingWork 會監督顯示程序,並防止任何群組中斷另一個群組的顯示作業。 字元變數 group 會標示不同群組的輸出,確認以預期的順序顯示結果。

Click 事件處理常式

每當使用者選擇 [開始] 按鈕,事件處理常式 StartButton_Click 就會增加群組字母。 處理常式接著會呼叫 AccessTheWebAsync 來執行下載作業。

Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)
    ' ***Verify that each group's results are displayed together, and that
    ' the groups display in order, by marking each group with a letter.
    group = ChrW(AscW(group) + 1)
    ResultsTextBox.Text &= String.Format(vbCrLf & vbCrLf & "#Starting group {0}.", group)

    Try
        ' *** Pass the group value to AccessTheWebAsync.
        Dim finishedGroup As Char = Await AccessTheWebAsync(group)

        ' The following line verifies a successful return from the download and
        ' display procedures.
        ResultsTextBox.Text &= String.Format(vbCrLf & vbCrLf & "#Group {0} is complete." & vbCrLf, finishedGroup)

    Catch ex As Exception
        ResultsTextBox.Text &= vbCrLf & "Downloads failed."

    End Try
End Sub

AccessTheWebAsync 方法

此範例會將 AccessTheWebAsync 分為兩個方法。 第一種方法為 AccessTheWebAsync,可啟動群組的所有下載工作,並且設定 pendingWork 來控制顯示程序。 此方法會使用 Language Integrated Query (LINQ 查詢) 和 ToArray 來同時啟動所有下載工作。

AccessTheWebAsync 接著呼叫 FinishOneGroupAsync 來等候每個下載完成,並顯示它的長度。

FinishOneGroupAsync 傳回工作,該工作指派給 AccessTheWebAsync 中的 pendingWork。 在工作完成前,該值會防止另一項作業中斷該工作。

Private Async Function AccessTheWebAsync(grp As Char) As Task(Of Char)

    Dim client = New HttpClient()

    ' Make a list of the web addresses to download.
    Dim urlList As List(Of String) = SetUpURLList()

    ' ***Kick off the downloads. The application of ToArray activates all the download tasks.
    Dim getContentTasks As Task(Of Byte())() =
        urlList.Select(Function(addr) client.GetByteArrayAsync(addr)).ToArray()

    ' ***Call the method that awaits the downloads and displays the results.
    ' Assign the Task that FinishOneGroupAsync returns to the gatekeeper task, pendingWork.
    pendingWork = FinishOneGroupAsync(urlList, getContentTasks, grp)

    ResultsTextBox.Text &=
        String.Format(vbCrLf & "#Task assigned for group {0}. Download tasks are active." & vbCrLf, grp)

    ' ***This task is complete when a group has finished downloading and displaying.
    Await pendingWork

    ' You can do other work here or just return.
    Return grp
End Function

FinishOneGroupAsync 方法

這個方法不斷循環群組中的下載工作,等待每一項、顯示下載網站的長度,並將長度加總到總計。

FinishOneGroupAsync 中的第一個陳述式使用 pendingWork,確保進入方法不會干擾已在顯示程序中的作業,或已在等候的作業。 如果這類作業正在進行,進入作業必須等候。

Private Async Function FinishOneGroupAsync(urls As List(Of String), contentTasks As Task(Of Byte())(), grp As Char) As Task

    ' Wait for the previous group to finish displaying results.
    If pendingWork IsNot Nothing Then
        Await pendingWork
    End If

    Dim total = 0

    ' contentTasks is the array of Tasks that was created in AccessTheWebAsync.
    For i As Integer = 0 To contentTasks.Length - 1
        ' Await the download of a particular URL, and then display the URL and
        ' its length.
        Dim content As Byte() = Await contentTasks(i)
        DisplayResults(urls(i), content, i, grp)
        total += content.Length
    Next

    ' Display the total count for all of the websites.
    ResultsTextBox.Text &=
        String.Format(vbCrLf & vbCrLf & "TOTAL bytes returned:  " & total & vbCrLf)
End Function

您可以執行這個範例,方法是將變更貼至建置應用程式中的程式碼,或者遵循下載應用程式的指示來下載範例,然後執行 QueueResults 專案。

參考資訊

在輸出中以井字號 (#) 開頭的資訊行會釐清此範例的運作方式。

輸出會顯示下列模式。

  • 當前一個群組顯示其輸出時,就可以啟動一個群組,但不會中斷前一個群組的輸出顯示。

    #Starting group A.
    #Task assigned for group A. Download tasks are active.
    
    A-1. msdn.microsoft.com/library/hh191443.aspx                87389
    A-2. msdn.microsoft.com/library/aa578028.aspx               207089
    A-3. msdn.microsoft.com/library/jj155761.aspx                30870
    A-4. msdn.microsoft.com/library/hh290140.aspx               119037
    A-5. msdn.microsoft.com/library/hh524395.aspx                71260
    
    #Starting group B.
    #Task assigned for group B. Download tasks are active.
    
    A-6. msdn.microsoft.com/library/ms404677.aspx               199186
    A-7. msdn.microsoft.com                                            53078
    A-8. msdn.microsoft.com/library/ff730837.aspx               148010
    
    TOTAL bytes returned:  915919
    
    B-1. msdn.microsoft.com/library/hh191443.aspx                87388
    B-2. msdn.microsoft.com/library/aa578028.aspx               207089
    B-3. msdn.microsoft.com/library/jj155761.aspx                30870
    
    #Group A is complete.
    
    B-4. msdn.microsoft.com/library/hh290140.aspx               119027
    B-5. msdn.microsoft.com/library/hh524395.aspx                71260
    B-6. msdn.microsoft.com/library/ms404677.aspx               199186
    B-7. msdn.microsoft.com                                            53078
    B-8. msdn.microsoft.com/library/ff730837.aspx               148010
    
    TOTAL bytes returned:  915908
    
  • 啟動 FinishOneGroupAsyncpendingWork 工作為 Nothing 的情況僅適用於優先啟動的 Group A。 當群組 A 到達 FinishOneGroupAsync 時,它尚未完成 await 運算式。 因此,尚未將控制項返回 AccessTheWebAsync,且尚未針對 pendingWork 進行第一次指派。

  • 下列兩行一律會在輸出中一起出現。 在 StartButton_Click 中啟動群組的作業,與將群組的工作指派給 pendingWork 之間,程式碼永遠不會中斷

    #Starting group B.
    #Task assigned for group B. Download tasks are active.
    

    在群組進入 StartButton_Click 後,作業尚未完成 await 運算式,需直到作業進入 FinishOneGroupAsync 為止。 因此,沒有其他作業可在該程式碼區段的過程中取得控制項。

檢閱及執行範例應用程式

若要進一步了解範例應用程式,您可以下載它、自行建置它,或檢閱本主題結尾的程式碼而不必實作應用程式。

注意

若要執行範例作為 Windows Presentation Foundation (WPF) 傳統型應用程式,您必須在電腦上安裝 Visual Studio 2012 或更新版本以及 .NET Framework 4.5 或更新版本。

下載應用程式

  1. 非同步範例︰重新進入 .NET 桌面應用程式下載壓縮檔案。

  2. 解壓縮您下載的檔案,然後啟動 Visual Studio。

  3. 在功能表列上,依序選擇 [檔案] 、[開啟舊檔] 及 [專案/方案]

  4. 導覽至保存解壓縮之範例程式碼的資料夾,然後開啟方案 (.sln) 檔案。

  5. 在方案總管中,開啟要執行之專案的捷徑功能表,然後選擇 [設定為啟始專案]

  6. 選擇 CTRL + F5 鍵以建置並執行專案。

建置應用程式

下節提供將範例建置為 WPF 應用程式的程式碼。

若要建置 WPF 應用程式
  1. 啟動 Visual Studio。

  2. 在功能表列上,選擇 [ 檔案]、[ 新增]、[ 專案]。

    [ 新增專案 ] 對話方塊隨即開啟。

  3. 在 [安裝的範本] 窗格中,依序展開 [Visual Basic] 及 [Windows]

  4. 在專案類型清單中,選擇 [WPF 應用程式]

  5. 將專案命名為 WebsiteDownloadWPF,選擇 .NET Framework 4.6 版或更新版本,然後按一下 [確定] 按鈕。

    新的專案隨即會出現在方案總管中。

  6. 在 Visual Studio 程式碼編輯器中,選擇 [ MainWindow.xaml ] 索引標籤。

    如未顯示索引標籤,請在方案總管中開啟 MainWindow.xaml 的捷徑功能表,然後選擇 [檢視程式碼]

  7. 在 MainWindow.xaml 的 [XAML] 檢視中,以下列程式碼取代程式碼。

    <Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:WebsiteDownloadWPF"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid Width="517" Height="360">
            <Button x:Name="StartButton" Content="Start" HorizontalAlignment="Left" Margin="-1,0,0,0" VerticalAlignment="Top" Click="StartButton_Click" Height="53" Background="#FFA89B9B" FontSize="36" Width="518"  />
            <TextBox x:Name="ResultsTextBox" HorizontalAlignment="Left" Margin="-1,53,0,-36" TextWrapping="Wrap" VerticalAlignment="Top" Height="343" FontSize="10" ScrollViewer.VerticalScrollBarVisibility="Visible" Width="518" FontFamily="Lucida Console" />
        </Grid>
    </Window>
    

    包含文字方塊和按鈕的簡易視窗會出現在 MainWindow.xaml 的 [設計] 檢視中。

  8. 在 [方案總管] 中,以滑鼠右鍵按一下 [參考],然後選取 [新增參考]

    如果尚未選取 System.Net.Http 的參考,請新增參考。

  9. 在 [方案總管] 中開啟 MainWindow.xaml.vb 的捷徑功能表,然後選擇 [檢視程式碼]

  10. 將 MainWindow.xaml.vb 中的程式碼更換為下列程式碼。

    ' Add the following Imports statements, and add a reference for System.Net.Http.
    Imports System.Net.Http
    Imports System.Threading
    
    Class MainWindow
    
        Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)
            System.Net.ServicePointManager.SecurityProtocol = System.Net.ServicePointManager.SecurityProtocol Or System.Net.SecurityProtocolType.Tls12
    
            ' This line is commented out to make the results clearer in the output.
            'ResultsTextBox.Text = ""
    
            Try
                Await AccessTheWebAsync()
    
            Catch ex As Exception
                ResultsTextBox.Text &= vbCrLf & "Downloads failed."
    
            End Try
        End Sub
    
        Private Async Function AccessTheWebAsync() As Task
    
            ' Declare an HttpClient object.
            Dim client = New HttpClient()
    
            ' Make a list of web addresses.
            Dim urlList As List(Of String) = SetUpURLList()
    
            Dim total = 0
            Dim position = 0
    
            For Each url In urlList
                ' GetByteArrayAsync returns a task. At completion, the task
                ' produces a byte array.
                Dim urlContents As Byte() = Await client.GetByteArrayAsync(url)
    
                position += 1
                DisplayResults(url, urlContents, position)
    
                ' Update the total.
                total += urlContents.Length
            Next
    
            ' Display the total count for all of the websites.
            ResultsTextBox.Text &=
                String.Format(vbCrLf & vbCrLf & "TOTAL bytes returned:  " & total & vbCrLf)
        End Function
    
        Private Function SetUpURLList() As List(Of String)
            Dim urls = New List(Of String) From
            {
                "https://msdn.microsoft.com/library/hh191443.aspx",
                "https://msdn.microsoft.com/library/aa578028.aspx",
                "https://msdn.microsoft.com/library/jj155761.aspx",
                "https://msdn.microsoft.com/library/hh290140.aspx",
                "https://msdn.microsoft.com/library/hh524395.aspx",
                "https://msdn.microsoft.com/library/ms404677.aspx",
                "https://msdn.microsoft.com",
                "https://msdn.microsoft.com/library/ff730837.aspx"
            }
            Return urls
        End Function
    
        Private Sub DisplayResults(url As String, content As Byte(), pos As Integer)
            ' Display the length of each website. The string format is designed
            ' to be used with a monospaced font, such as Lucida Console or
            ' Global Monospace.
    
            ' Strip off the "http:'".
            Dim displayURL = url.Replace("https://", "")
            ' Display position in the URL list, the URL, and the number of bytes.
            ResultsTextBox.Text &= String.Format(vbCrLf & "{0}. {1,-58} {2,8}", pos, displayURL, content.Length)
        End Sub
    End Class
    
  11. 選擇 CTRL+F5 鍵以執行程式,然後選擇 [開始] 按鈕數次。

  12. 停用開始按鈕取消後再重新啟動作業執行多個作業並將輸出加入佇列進行變更以處理重新進入。

另請參閱