Bagikan melalui


Cara: Memperluas Panduan Asinkron dengan Menggunakan Task.WhenAll (Visual Basic)

Anda dapat meningkatkan performa solusi asinkron di Walkthrough: Mengakses Web dengan Menggunakan Asinkron dan Menunggu (Visual Basic) dengan menggunakan metode Task.WhenAll. Metode ini secara asinkron menunggu beberapa operasi asinkron, yang diwakili sebagai kumpulan tugas.

Anda mungkin telah memperhatikan dalam panduan bahwa situs web diunduh dengan tarif yang berbeda. Terkadang salah satu situs web sangat lambat, yang menunda semua unduhan yang tersisa. Ketika Anda menjalankan solusi asinkron yang Anda bangun di panduan, Anda dapat mengakhiri program dengan mudah jika Anda tidak ingin menunggu, tetapi opsi yang lebih baik adalah memulai semua unduhan pada saat yang sama dan membiarkan unduhan yang lebih cepat berlanjut tanpa menunggu yang tertunda.

Anda menerapkan metode Task.WhenAll pada kumpulan tugas. Penerapan WhenAll mengembalikan satu tugas yang tidak selesai sampai setiap tugas dalam kumpulan tersebut selesai. Tugas tampaknya berjalan secara paralel, tetapi tidak ada thread tambahan yang dibuat. Tugas dapat diselesaikan dalam urutan apa pun.

Penting

Prosedur berikut menjelaskan ekstensi ke aplikasi asinkron yang dikembangkan di Panduan: Mengakses Web dengan Menggunakan Asinkron dan Menunggu (Visual Basic). Anda dapat mengembangkan aplikasi dengan menyelesaikan panduan atau mengunduh sampel dari Browser Sampel .NET. Contoh kode ada dalam proyek SerialAsyncExample.

Untuk menjalankan contoh, Anda harus menginstal Visual Studio 2012 atau yang lebih baru di komputer Anda.

Untuk menambahkan Task.WhenAll ke solusi GetURLContentsAsync Anda

  1. ProcessURLAsync Tambahkan metode ke aplikasi pertama yang dikembangkan di Walkthrough: Mengakses Web dengan Menggunakan Asinkron dan Tunggu (Visual Basic).

    • Jika Anda mengunduh kode dari Sampel Kode Pengembang, buka proyek AsyncWalkthrough, lalu tambahkan ProcessURLAsync ke file MainWindow.xaml.vb.

    • Jika Anda mengembangkan kode dengan menyelesaikan panduan, tambahkan ProcessURLAsync ke aplikasi yang menyertakan metode GetURLContentsAsync. File MainWindow.xaml.vb untuk aplikasi ini adalah contoh pertama di bagian "Contoh Kode Lengkap dari Panduan".

    Metode ProcessURLAsync mengonsolidasikan aksi di bagian inti dari For Each loop dalam SumPageSizesAsync walkthrough asli. Metode ini secara asinkron mengunduh konten situs web tertentu sebagai array byte, lalu menampilkan dan mengembalikan panjang array byte.

    Private Async Function ProcessURLAsync(url As String) As Task(Of Integer)
    
        Dim byteArray = Await GetURLContentsAsync(url)
        DisplayResults(url, byteArray)
        Return byteArray.Length
    End Function
    
  2. Beri komentar atau hapus For Each perulangan di SumPageSizesAsync, seperti yang ditunjukkan kode berikut ini.

    'Dim total = 0
    'For Each url In urlList
    
    '    Dim urlContents As Byte() = Await GetURLContentsAsync(url)
    
    '    ' The previous line abbreviates the following two assignment statements.
    
    '    ' GetURLContentsAsync returns a task. At completion, the task
    '    ' produces a byte array.
    '    'Dim getContentsTask As Task(Of Byte()) = GetURLContentsAsync(url)
    '    'Dim urlContents As Byte() = Await getContentsTask
    
    '    DisplayResults(url, urlContents)
    
    '    ' Update the total.
    '    total += urlContents.Length
    'Next
    
  3. Membuat kumpulan tugas. Kode berikut mendefinisikan kueri yang, saat dijalankan oleh ToArray metode , membuat kumpulan tugas yang mengunduh konten setiap situs web. Tugas dimulai saat kueri dievaluasi.

    Tambahkan kode berikut ke metode SumPageSizesAsync setelah deklarasi urlList.

    ' Create a query.
    Dim downloadTasksQuery As IEnumerable(Of Task(Of Integer)) =
        From url In urlList Select ProcessURLAsync(url)
    
    ' Use ToArray to execute the query and start the download tasks.
    Dim downloadTasks As Task(Of Integer)() = downloadTasksQuery.ToArray()
    
  4. Terapkan Task.WhenAll ke kumpulan tugas, downloadTasks. Task.WhenAll mengembalikan satu tugas yang selesai ketika semua tugas dalam kumpulan tugas telah selesai.

    Dalam contoh berikut, ekspresi Await menunggu hingga tugas tunggal yang dikembalikan oleh WhenAll selesai. Ekspresi ini akan dievaluasi menjadi array nilai bilangan bulat, di mana setiap nilai bilangan bulat adalah panjang dari setiap situs web yang diunduh. Tambahkan kode berikut ke SumPageSizesAsync, tepat setelah kode yang Anda tambahkan di langkah sebelumnya.

    ' Await the completion of all the running tasks.
    Dim lengths As Integer() = Await Task.WhenAll(downloadTasks)
    
    '' The previous line is equivalent to the following two statements.
    'Dim whenAllTask As Task(Of Integer()) = Task.WhenAll(downloadTasks)
    'Dim lengths As Integer() = Await whenAllTask
    
  5. Terakhir, gunakan Sum metode untuk menghitung jumlah panjang semua situs web. Tambahkan baris berikut ke SumPageSizesAsync.

    Dim total = lengths.Sum()
    

Untuk menambahkan Task.WhenAll ke solusi HttpClient.GetByteArrayAsync

  1. Tambahkan versi ProcessURLAsync berikut ke aplikasi kedua yang dikembangkan di Walkthrough: Mengakses Web dengan Menggunakan Asinkron dan Tunggu (Visual Basic).

    • Jika Anda mengunduh kode dari Sampel Kode Pengembang, buka proyek AsyncWalkthrough_HttpClient, lalu tambahkan ProcessURLAsync ke file MainWindow.xaml.vb.

    • Jika Anda mengembangkan kode dengan menyelesaikan panduan langkah demi langkah, tambahkan ProcessURLAsync pada aplikasi yang menggunakan metode HttpClient.GetByteArrayAsync. File MainWindow.xaml.vb untuk aplikasi ini adalah contoh kedua di bagian "Contoh Kode Lengkap dari Panduan".

    Metode ProcessURLAsync mengonsolidasikan aksi di bagian inti dari For Each loop dalam SumPageSizesAsync walkthrough asli. Metode ini secara asinkron mengunduh konten situs web tertentu sebagai array byte, lalu menampilkan dan mengembalikan panjang array byte.

    Satu-satunya perbedaan dari metode ProcessURLAsync dalam prosedur sebelumnya adalah penggunaan objek HttpClient, client.

    Private Async Function ProcessURLAsync(url As String, client As HttpClient) As Task(Of Integer)
    
        Dim byteArray = Await client.GetByteArrayAsync(url)
        DisplayResults(url, byteArray)
        Return byteArray.Length
    End Function
    
  2. Beri komentar atau hapus For Each perulangan di SumPageSizesAsync, seperti yang ditunjukkan kode berikut ini.

    'Dim total = 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)
    
    '    ' The following two lines can replace the previous assignment statement.
    '    'Dim getContentsTask As Task(Of Byte()) = client.GetByteArrayAsync(url)
    '    'Dim urlContents As Byte() = Await getContentsTask
    
    '    DisplayResults(url, urlContents)
    
    '    ' Update the total.
    '    total += urlContents.Length
    'Next
    
  3. Tentukan kueri yang, saat dijalankan oleh ToArray metode , membuat kumpulan tugas yang mengunduh konten setiap situs web. Tugas dimulai saat kueri dievaluasi.

    Tambahkan kode berikut ke metode SumPageSizesAsync setelah deklarasi client dan urlList.

    ' Create a query.
    Dim downloadTasksQuery As IEnumerable(Of Task(Of Integer)) =
        From url In urlList Select ProcessURLAsync(url, client)
    
    ' Use ToArray to execute the query and start the download tasks.
    Dim downloadTasks As Task(Of Integer)() = downloadTasksQuery.ToArray()
    
  4. Selanjutnya, terapkan Task.WhenAll ke kumpulan tugas, downloadTasks. Task.WhenAll mengembalikan satu tugas yang selesai ketika semua tugas dalam kumpulan tugas telah selesai.

    Dalam contoh berikut, ekspresi Await menunggu hingga tugas tunggal yang dikembalikan oleh WhenAll selesai. Setelah selesai, ekspresi Await dievaluasi menjadi array bilangan bulat, di mana setiap bilangan bulat adalah panjang masing-masing situs web yang diunduh. Tambahkan kode berikut ke SumPageSizesAsync, tepat setelah kode yang Anda tambahkan di langkah sebelumnya.

    ' Await the completion of all the running tasks.
    Dim lengths As Integer() = Await Task.WhenAll(downloadTasks)
    
    '' The previous line is equivalent to the following two statements.
    'Dim whenAllTask As Task(Of Integer()) = Task.WhenAll(downloadTasks)
    'Dim lengths As Integer() = Await whenAllTask
    
  5. Terakhir, gunakan Sum metode untuk mendapatkan jumlah panjang semua situs web. Tambahkan baris berikut ke SumPageSizesAsync.

    Dim total = lengths.Sum()
    

Untuk menguji solusi Task.WhenAll

Untuk salah satu solusi, pilih kunci F5 untuk menjalankan program, lalu pilih tombol Mulai . Output harus menyerupai output dari solusi asinkron di Walkthrough: Mengakses Web dengan Menggunakan Asinkron dan Menunggu (Visual Basic). Namun, perhatikan bahwa situs web muncul dalam urutan yang berbeda setiap kali.

Contoh 1

Kode berikut menunjukkan ekstensi ke proyek yang menggunakan GetURLContentsAsync metode untuk mengunduh konten dari web.

' Add the following Imports statements, and add a reference for System.Net.Http.
Imports System.Net.Http
Imports System.Net
Imports System.IO

Class MainWindow

    Async Sub startButton_Click(sender As Object, e As RoutedEventArgs) Handles startButton.Click

        resultsTextBox.Clear()

        ' One-step async call.
        Await SumPageSizesAsync()

        '' Two-step async call.
        'Dim sumTask As Task = SumPageSizesAsync()
        'Await sumTask

        resultsTextBox.Text &= vbCrLf & "Control returned to button1_Click."
    End Sub

    Private Async Function SumPageSizesAsync() As Task

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

        ' Create a query.
        Dim downloadTasksQuery As IEnumerable(Of Task(Of Integer)) =
            From url In urlList Select ProcessURLAsync(url)

        ' Use ToArray to execute the query and start the download tasks.
        Dim downloadTasks As Task(Of Integer)() = downloadTasksQuery.ToArray()

        ' You can do other work here before awaiting.

        ' Await the completion of all the running tasks.
        Dim lengths As Integer() = Await Task.WhenAll(downloadTasks)

        '' The previous line is equivalent to the following two statements.
        'Dim whenAllTask As Task(Of Integer()) = Task.WhenAll(downloadTasks)
        'Dim lengths As Integer() = Await whenAllTask

        Dim total = lengths.Sum()

        'Dim total = 0
        'For Each url In urlList

        '    Dim urlContents As Byte() = Await GetURLContentsAsync(url)

        '    ' The previous line abbreviates the following two assignment statements.

        '    ' GetURLContentsAsync returns a task. At completion, the task
        '    ' produces a byte array.
        '    'Dim getContentsTask As Task(Of Byte()) = GetURLContentsAsync(url)
        '    'Dim urlContents As Byte() = Await getContentsTask

        '    DisplayResults(url, urlContents)

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

        ' Display the total count for all of the web addresses.
        resultsTextBox.Text &= String.Format(vbCrLf & vbCrLf &
                                             "Total bytes returned:  {0}" & vbCrLf, total)
    End Function

    Private Function SetUpURLList() As List(Of String)

        Dim urls = New List(Of String) From
            {
                "https://msdn.microsoft.com",
                "https://msdn.microsoft.com/library/hh290136.aspx",
                "https://msdn.microsoft.com/library/ee256749.aspx",
                "https://msdn.microsoft.com/library/hh290138.aspx",
                "https://msdn.microsoft.com/library/hh290140.aspx",
                "https://msdn.microsoft.com/library/dd470362.aspx",
                "https://msdn.microsoft.com/library/aa578028.aspx",
                "https://msdn.microsoft.com/library/ms404677.aspx",
                "https://msdn.microsoft.com/library/ff730837.aspx"
            }
        Return urls
    End Function

    ' The actions from the foreach loop are moved to this async method.
    Private Async Function ProcessURLAsync(url As String) As Task(Of Integer)

        Dim byteArray = Await GetURLContentsAsync(url)
        DisplayResults(url, byteArray)
        Return byteArray.Length
    End Function

    Private Async Function GetURLContentsAsync(url As String) As Task(Of Byte())

        ' The downloaded resource ends up in the variable named content.
        Dim content = New MemoryStream()

        ' Initialize an HttpWebRequest for the current URL.
        Dim webReq = CType(WebRequest.Create(url), HttpWebRequest)

        ' Send the request to the Internet resource and wait for
        ' the response.
        Using response As WebResponse = Await webReq.GetResponseAsync()
            ' Get the data stream that is associated with the specified URL.
            Using responseStream As Stream = response.GetResponseStream()
                ' Read the bytes in responseStream and copy them to content.
                ' CopyToAsync returns a Task, not a Task<T>.
                Await responseStream.CopyToAsync(content)
            End Using
        End Using

        ' Return the result as a byte array.
        Return content.ToArray()
    End Function

    Private Sub DisplayResults(url As String, content As Byte())

        ' 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.
        Dim bytes = content.Length
        ' Strip off the "https://".
        Dim displayURL = url.Replace("https://", "")
        resultsTextBox.Text &= String.Format(vbCrLf & "{0,-58} {1,8}", displayURL, bytes)
    End Sub

End Class

Contoh 2

Kode berikut menunjukkan ekstensi ke proyek yang menggunakan metode HttpClient.GetByteArrayAsync untuk mengunduh konten dari web.

' Add the following Imports statements, and add a reference for System.Net.Http.
Imports System.Net.Http
Imports System.Net
Imports System.IO

Class MainWindow

    Async Sub startButton_Click(sender As Object, e As RoutedEventArgs) Handles startButton.Click

        resultsTextBox.Clear()

        '' One-step async call.
        Await SumPageSizesAsync()

        '' Two-step async call.
        'Dim sumTask As Task = SumPageSizesAsync()
        'Await sumTask

        resultsTextBox.Text &= vbCrLf & "Control returned to button1_Click."
    End Sub

    Private Async Function SumPageSizesAsync() As Task

        ' Declare an HttpClient object and increase the buffer size. The
        ' default buffer size is 65,536.
        Dim client As HttpClient =
            New HttpClient() With {.MaxResponseContentBufferSize = 1000000}

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

        ' Create a query.
        Dim downloadTasksQuery As IEnumerable(Of Task(Of Integer)) =
            From url In urlList Select ProcessURLAsync(url, client)

        ' Use ToArray to execute the query and start the download tasks.
        Dim downloadTasks As Task(Of Integer)() = downloadTasksQuery.ToArray()

        ' You can do other work here before awaiting.

        ' Await the completion of all the running tasks.
        Dim lengths As Integer() = Await Task.WhenAll(downloadTasks)

        '' The previous line is equivalent to the following two statements.
        'Dim whenAllTask As Task(Of Integer()) = Task.WhenAll(downloadTasks)
        'Dim lengths As Integer() = Await whenAllTask

        Dim total = lengths.Sum()

        ''<snippet7>
        'Dim total = 0
        'For Each url In urlList
        '    ' GetByteArrayAsync returns a task. At completion, the task
        '    ' produces a byte array.
        '    '<snippet31>
        '    Dim urlContents As Byte() = Await client.GetByteArrayAsync(url)
        '    '</snippet31>

        '    ' The following two lines can replace the previous assignment statement.
        '    'Dim getContentsTask As Task(Of Byte()) = client.GetByteArrayAsync(url)
        '    'Dim urlContents As Byte() = Await getContentsTask

        '    DisplayResults(url, urlContents)

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

        ' Display the total count for all of the web addresses.
        resultsTextBox.Text &= String.Format(vbCrLf & vbCrLf &
                                             "Total bytes returned:  {0}" & vbCrLf, total)
    End Function

    Private Function SetUpURLList() As List(Of String)

        Dim urls = New List(Of String) From
            {
                "https://www.msdn.com",
                "https://msdn.microsoft.com/library/hh290136.aspx",
                "https://msdn.microsoft.com/library/ee256749.aspx",
                "https://msdn.microsoft.com/library/hh290138.aspx",
                "https://msdn.microsoft.com/library/hh290140.aspx",
                "https://msdn.microsoft.com/library/dd470362.aspx",
                "https://msdn.microsoft.com/library/aa578028.aspx",
                "https://msdn.microsoft.com/library/ms404677.aspx",
                "https://msdn.microsoft.com/library/ff730837.aspx"
            }
        Return urls
    End Function

    Private Async Function ProcessURLAsync(url As String, client As HttpClient) As Task(Of Integer)

        Dim byteArray = Await client.GetByteArrayAsync(url)
        DisplayResults(url, byteArray)
        Return byteArray.Length
    End Function

    Private Sub DisplayResults(url As String, content As Byte())

        ' 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.
        Dim bytes = content.Length
        ' Strip off the "https://".
        Dim displayURL = url.Replace("https://", "")
        resultsTextBox.Text &= String.Format(vbCrLf & "{0,-58} {1,8}", displayURL, bytes)
    End Sub

End Class

Lihat juga