Compartilhar via


Tratando a reentrada em aplicativos assíncronos (Visual Basic)

Ao incluir código assíncrono em seu aplicativo, você deve considerar e, possivelmente, evitar a reentrância, que se refere à reinserção de uma operação assíncrona antes de ela ser concluída. Se você não identificar e lidar com as possibilidades de reentrada, isso poderá causar resultados inesperados.

Observação

Para executar o exemplo, você deve ter o Visual Studio 2012 ou mais recente e o .NET Framework 4.5 ou mais recente instalado em seu computador.

Observação

O TLS (Transport Layer Security) versão 1.2 agora é a versão mínima a ser usada no desenvolvimento do aplicativo. Se seu aplicativo tiver como destino uma versão do .NET Framework anterior à 4.7, consulte o artigo a seguir para as práticas recomendadas do TLS (Transport Layer Security) com o .NET Framework.

Reconhecendo a reentrância

No exemplo deste tópico, os usuários escolhem um botão Iniciar para iniciar um aplicativo assíncrono que baixa uma série de sites e calcula o número total de bytes baixados. Uma versão síncrona do exemplo responderia da mesma maneira, independentemente de quantas vezes um usuário escolher o botão porque, após a primeira vez, o thread da interface do usuário ignora esses eventos até que o aplicativo termine de ser executado. Em um aplicativo assíncrono, no entanto, o thread da interface continua respondendo, e você pode reentrar na operação assíncrona antes que ela seja concluída.

O exemplo a seguir mostra a saída esperada se o usuário escolher o botão Iniciar apenas uma vez. Uma lista dos sites baixados aparece com o tamanho, em bytes, de cada site. O número total de bytes é exibido no final.

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

No entanto, se o usuário escolher o botão mais de uma vez, o manipulador de eventos será invocado repetidamente e o processo de download será reentrado sempre. Como resultado, várias operações assíncronas são executadas ao mesmo tempo, a saída intercala os resultados, e o número total de bytes é confuso.

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

Você pode examinar o código que produz essa saída rolando até o final deste tópico. Você pode experimentar o código baixando a solução para seu computador local e, em seguida, executando o projeto WebsiteDownload ou usando o código no final deste tópico para criar seu próprio projeto Para obter mais informações e instruções, consulte Revisão e execução do aplicativo de exemplo.

Tratando a reentrância

É possível tratar a reentrância de várias maneiras, dependendo do que você deseja que seu aplicativo faça. Este tópico apresenta os seguintes exemplos:

  • Desabilitar o botão Iniciar

    Desabilite o botão Iniciar enquanto a operação estiver em execução para que o usuário não possa interrompê-lo.

  • Cancelar e reiniciar a operação

    Cancele qualquer operação que ainda esteja em execução quando o usuário escolher o botão Iniciar novamente e, em seguida, permitir que a operação solicitada mais recentemente continue.

  • Executar várias operações e colocar a saída na fila

    Permitir que todas as operações solicitadas sejam executadas de forma assíncrona, mas coordene a exibição da saída para que os resultados de cada operação apareçam juntos e em ordem.

Desabilitar o botão Iniciar

Você pode bloquear o botão Iniciar enquanto uma operação está em execução desabilitando o botão na parte superior do StartButton_Click manipulador de eventos. Em seguida, você pode ativar novamente o botão dentro de um bloco Finally quando a operação for concluída, permitindo que os usuários executem o aplicativo novamente.

O código a seguir mostra essas alterações, que são marcadas com asteriscos. Você pode adicionar as alterações ao código no final deste tópico ou baixar o programa concluído em Amostras Assíncronas: Reentrância em Aplicativos Desktop do .NET. O nome do projeto é 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

Como resultado das alterações, o botão não responde enquanto AccessTheWebAsync baixa os sites, portanto, o processo não pode ser reentrado.

Cancelar e reiniciar a operação

Em vez de desabilitar o botão Iniciar , você pode manter o botão ativo, mas, se o usuário escolher esse botão novamente, cancele a operação que já está em execução e permita que a operação iniciada mais recentemente continue.

Para obter mais informações sobre cancelamento, consulte Fine-Tuning Seu Aplicativo Assíncrono (Visual Basic).

Para configurar esse cenário, faça as seguintes alterações no código básico fornecido na revisão e execução do aplicativo de exemplo. Você também pode baixar o aplicativo finalizado de Exemplos assíncronos: reentrância em aplicativos de área de trabalho do .NET. O nome deste projeto é CancelAndRestart.

  1. Declare uma CancellationTokenSource variável, ctsque está no escopo de todos os métodos.

    Class MainWindow // Or Class MainPage
    
        ' *** Declare a System.Threading.CancellationTokenSource.
        Dim cts As CancellationTokenSource
    
  2. Em StartButton_Click, determine se uma operação já está em andamento. Se o valor de cts for Nothing, nenhuma operação está ativa. Se o valor não for Nothing, a operação que já está em execução será cancelada.

    ' *** If a download process is already underway, cancel it.
    If cts IsNot Nothing Then
        cts.Cancel()
    End If
    
  3. Defina cts como um valor diferente que representa o processo atual.

    ' *** Now set cts to cancel the current process if the button is chosen again.
    Dim newCTS As CancellationTokenSource = New CancellationTokenSource()
    cts = newCTS
    
  4. No final de StartButton_Click, o processo atual está concluído, portanto, defina o valor de cts para Nothing novamente.

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

O código a seguir mostra todas as alterações em StartButton_Click. As adições são marcadas com asteriscos.

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

In AccessTheWebAsync, faça as seguintes alterações.

  • Adicione um parâmetro para aceitar o token de cancelamento de StartButton_Click.

  • Use o GetAsync método para baixar os sites porque GetAsync aceita um CancellationToken argumento.

  • Antes de chamar DisplayResults para exibir os resultados de cada site baixado, verifique ct se a operação atual não foi cancelada.

O código a seguir mostra essas alterações, que são marcadas com asteriscos.

' *** 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

Se você escolher o botão Iniciar várias vezes enquanto este aplicativo estiver em execução, ele deverá produzir resultados semelhantes à seguinte saída:

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

Para eliminar as listas parciais, descompacte a primeira linha de código StartButton_Click para limpar a caixa de texto sempre que o usuário reiniciar a operação.

Executar múltiplas operações e enfileirar o resultado

Este terceiro exemplo é o mais complicado porque o aplicativo inicia outra operação assíncrona sempre que o usuário escolhe o botão Iniciar e todas as operações são executadas até a conclusão. Todas as operações solicitadas baixam sites da lista de forma assíncrona, mas a saída das operações é apresentada sequencialmente. Ou seja, a atividade de download real é intercalada, conforme mostrado na saída em Reconhecendo a reentrância, mas a lista de resultados para cada grupo é apresentada separadamente.

As operações compartilham uma Task global, pendingWork, que serve como um gatekeeper para o processo de exibição.

Você pode executar este exemplo colando as alterações no código na criação do aplicativo ou pode seguir as instruções para baixar o aplicativo para baixar o exemplo e, em seguida, executar o projeto QueueResults.

A saída a seguir mostra o resultado se o usuário escolher o botão Iniciar apenas uma vez. O rótulo de letra, A, indica que o resultado é da primeira vez que o botão Iniciar é escolhido. Os números mostram a ordem das URLs na lista de destinos de download.

#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.

Se o usuário escolher o botão Iniciar três vezes, o aplicativo produzirá uma saída semelhante às linhas a seguir. As linhas de informações que começam com um sinal de libra (#) rastreiam o progresso do aplicativo.

#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.

Os grupos B e C iniciam antes da conclusão do grupo A, mas a saída para cada grupo é exibida separadamente. Toda a saída do grupo A é exibida primeiro, seguida por toda a saída do grupo B e, em seguida, toda a saída para o grupo C. O aplicativo sempre exibe os grupos em ordem e, para cada grupo, sempre exibe as informações sobre os sites individuais na ordem em que as URLs aparecem na lista de URLs.

No entanto, você não pode prever a ordem na qual os downloads realmente acontecem. Depois que vários grupos forem iniciados, as tarefas de download geradas por eles estarão ativas. Não é possível supor que o A-1 será baixado antes do B-1 e não é possível supor que o A-1 será baixado antes do A-2.

Definições globais

O código de exemplo contém as duas declarações globais a seguir visíveis de todos os métodos.

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)

A Task variável pendingWorksupervisiona o processo de exibição e impede que qualquer grupo interrompa a operação de exibição de outro grupo. A variável de caractere, group, rotula a saída de grupos diferentes para verificar se os resultados aparecem na ordem esperada.

O manipulador de eventos Click

O manipulador StartButton_Clickde eventos incrementa a letra do grupo sempre que o usuário escolhe o botão Iniciar . Em seguida, o manipulador chama AccessTheWebAsync para executar a operação de download.

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

O método AccessTheWebAsync

Este exemplo é dividido AccessTheWebAsync em dois métodos. O primeiro método, AccessTheWebAsync, inicia todas as tarefas de download de um grupo e configura pendingWork para controlar o processo de exibição. O método usa uma consulta LINQ e ToArray para iniciar todas as tarefas de download ao mesmo tempo.

AccessTheWebAsync em seguida, chama FinishOneGroupAsync para aguardar a conclusão de cada download e exibir seu comprimento.

FinishOneGroupAsync retorna uma tarefa que é atribuída a pendingWork em AccessTheWebAsync. Esse valor impede a interrupção por outra operação antes que a tarefa seja concluída.

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

O método FinishOneGroupAsync

Esse método percorre as tarefas de download em um grupo, aguardando cada uma delas, exibindo o comprimento do site baixado e adicionando o comprimento ao total.

A primeira instrução em FinishOneGroupAsync usa pendingWork para verificar se o método de inserção não interfere com uma operação que já está no processo de exibição ou que já está aguardando. Se essa operação estiver em andamento, a operação de entrada deverá aguardar sua vez.

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

Você pode executar este exemplo colando as alterações no código na criação do aplicativo ou pode seguir as instruções para baixar o aplicativo para baixar o exemplo e, em seguida, executar o projeto QueueResults.

Pontos de interesse

As linhas de informações que começam com um sinal de libra (#) na saída esclarecem como este exemplo funciona.

A saída mostra os seguintes padrões.

  • Um grupo pode ser iniciado enquanto um grupo anterior está exibindo sua saída, mas a exibição da saída do grupo anterior não é interrompida.

    #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
    
  • O tarefa pendingWork é Nothing no início de FinishOneGroupAsync somente para o grupo A, que foi iniciado primeiro. O grupo A ainda não concluiu uma expressão await quando alcança o FinishOneGroupAsync. Portanto, o controle não retornou AccessTheWebAsynce a primeira atribuição a pendingWork não ocorreu.

  • As duas linhas a seguir sempre aparecem juntas na saída. O código nunca é interrompido entre iniciar a operação de um grupo em StartButton_Click e atribuir uma tarefa ao grupo em pendingWork.

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

    Depois que um grupo insere o StartButton_Click, a operação não conclui uma expressão await até que a operação insira FinishOneGroupAsync. Portanto, nenhuma outra operação pode obter controle durante esse segmento de código.

Examinar e executar o aplicativo de exemplo

Para entender melhor o aplicativo de exemplo, você pode baixá-lo, compilá-lo por conta própria ou examinar o código no final deste tópico sem implementar o aplicativo.

Observação

Para executar o exemplo como um aplicativo de área de trabalho do WPF (Windows Presentation Foundation), você deve ter o Visual Studio 2012 ou mais recente e o .NET Framework 4.5 ou mais recente instalado em seu computador.

Baixando o aplicativo

  1. Baixe o aplicativo compactado em Exemplos assíncronos: reentrância em aplicativos de área de trabalho do .NET.

  2. Descompacte o arquivo que você baixou e inicie o Visual Studio.

  3. Na barra de menus, escolha Arquivo, Abrir, Projeto/Solução.

  4. Navegue até a pasta que contém o código de exemplo descompactado e abra o arquivo de solução (.sln).

  5. No Gerenciador de Soluções, abra o menu de atalho do projeto que você deseja executar e escolha Definir como StartUpProject.

  6. Escolha as teclas CTRL+F5 para compilar e executar o projeto.

Compilando o aplicativo

A seção a seguir fornece o código para criar o exemplo como um aplicativo WPF.

Para criar um aplicativo WPF
  1. Inicie o Visual Studio.

  2. Na barra de menus, escolha Arquivo, Novo, Projeto.

    A caixa de diálogo Novo Projeto é aberta.

  3. No painel Modelos Instalados , expanda o Visual Basic e expanda o Windows.

  4. Na lista de tipos de projeto, escolha Aplicativo WPF.

  5. Nomeie o projeto WebsiteDownloadWPF, escolha a versão do .NET Framework 4.6 ou superior e clique no botão OK .

    O novo projeto aparece no Gerenciador de Soluções.

  6. No Editor de Código do Visual Studio, escolha a guia MainWindow.xaml.

    Se a guia não estiver visível, abra o menu de atalho para MainWindow.xaml no Gerenciador de Soluções e escolha Exibir Código.

  7. Na exibição XAML de MainWindow.xaml, substitua o código pelo código a seguir.

    <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>
    

    Uma janela simples que contém uma caixa de texto e um botão aparece no modo de exibição Design de MainWindow.xaml.

  8. No Gerenciador de Soluções, clique com o botão direito do mouse em Referências e selecione Adicionar Referência.

    Adicione uma referência para System.Net.Http, se ela ainda não estiver selecionada.

  9. No Gerenciador de Soluções, abra o menu de atalho para MainWindow.xaml.vb e escolha Exibir Código.

  10. Em MainWindow.xaml.vb, substitua o código pelo código a seguir.

    ' 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. Escolha as teclas CTRL+F5 para executar o programa e escolha o botão Iniciar várias vezes.

  12. Faça as alterações de Desabilitar o botão Iniciar, Cancelar e reiniciar a operação ou Executar várias operações e colocar a saída em fila para tratar a reentrância.

Consulte também