Partilhar via


Manipulando a reentrância 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 a reinserir uma operação assíncrona antes que ela seja concluída. Se você não identificar e lidar com possibilidades de reentrância, isso pode causar resultados inesperados.

Nota

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

Nota

Transport Layer Security (TLS) versão 1.2 é agora a versão mínima a ser usada no desenvolvimento do seu aplicativo. Se seu aplicativo tiver como destino uma versão do .NET Framework anterior à 4.7, consulte o seguinte artigo para obter as práticas recomendadas de 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 escolhe o botão porque, após a primeira vez, o thread da interface do usuário ignora esses eventos até que o aplicativo termine a execução. Em um aplicativo assíncrono, no entanto, o thread da interface do usuário continua a responder e você pode reinserir a 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 aparece 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á reinserido a cada vez. 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 revisar 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 Revisando e executando o aplicativo de exemplo.

Manuseamento da Reentrância

Você pode lidar com a reentrância de várias maneiras, dependendo do que você deseja que seu aplicativo faça. Este tópico apresenta os seguintes exemplos:

  • Desative o botão Iniciar

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

  • 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, deixe a operação solicitada mais recentemente continuar.

  • Executar várias operações e enfileirar a saída

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

Desative o botão Iniciar

Você pode bloquear o botão Iniciar enquanto uma operação está em execução desativando o botão na parte superior do StartButton_Click manipulador de eventos. Em seguida, você pode reativar o botão de dentro de um Finally bloco quando a operação terminar para que os usuários possam executar 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 pode baixar o aplicativo concluído de Async Samples: Reentrancy in .NET Desktop Apps. 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 está baixando os sites, portanto, o processo não pode ser reinserido.

Cancelar e reiniciar a operação

Em vez de desativar 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 deixe a operação iniciada mais recentemente continuar.

Para obter mais informações sobre cancelamento, consulte Ajustando seu aplicativo assíncrono (Visual Basic).

Para configurar esse cenário, faça as seguintes alterações no código básico fornecido em Examinando e executando o aplicativo de exemplo. Você também pode baixar o aplicativo concluído de Async Samples: Reentrancy in .NET Desktop Apps. O nome deste projeto é CancelAndRestart.

  1. Declare uma CancellationTokenSource variável, cts, que 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 é Nothing, nenhuma operação já está ativa. Se o valor não Nothingfor , 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 do StartButton_Click, o processo atual está concluído, portanto, defina o valor de cts volta para Nothing.

    ' *** 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 no StartButton_Click. Os aditamentos estão assinalados 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

No 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 ligar 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, descomente a primeira linha de código para StartButton_Click limpar a caixa de texto sempre que o usuário reiniciar a operação.

Executar várias operações e enfileirar a saída

Este terceiro exemplo é o mais complicado, pois o aplicativo inicia outra operação assíncrona cada vez 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, como mostra a saída em Reconhecendo Reentrância , mas a lista de resultados para cada grupo é apresentada separadamente.

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

Você pode executar este exemplo colando as alterações no código em Criando o aplicativo ou pode seguir as instruções em 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 da letra, A, indica que o resultado é da primeira vez que o botão Iniciar é escolhido. Os números mostram a ordem dos 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ção 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 começam antes do grupo A terminar, mas a saída para cada grupo aparece separadamente. Toda a saída para o grupo A aparece primeiro, seguida por toda a saída para o 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 os URLs aparecem na lista de URLs.

No entanto, você não pode prever a ordem em que os downloads realmente acontecem. Depois que vários grupos forem iniciados, as tarefas de download que eles geram estarão todas ativas. Você não pode assumir que A-1 será baixado antes de B-1, e você não pode assumir que A-1 será baixado antes de A-2.

Definições globais

O código de exemplo contém as duas declarações globais a seguir que são 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, pendingWork, supervisiona 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 diferentes grupos para verificar se os resultados aparecem na ordem esperada.

O manipulador de eventos Click

O manipulador de eventos, StartButton_Click, incrementa a letra do grupo cada vez 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 se AccessTheWebAsync divide em dois métodos. O primeiro método, AccessTheWebAsync, inicia todas as tarefas de download para um grupo e configura-se pendingWork para controlar o processo de exibição. O método usa uma consulta integrada de linguagem (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 sua duração.

FinishOneGroupAsyncRetorna uma tarefa atribuída ao pendingWork .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

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

A primeira instrução em FinishOneGroupAsync usa pendingWork para garantir que a inserção do método não interfira com uma operação que já está no processo de exibição ou que já está esperando. Se tal operação estiver em andamento, a operação de entrada deve 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 em Criando o aplicativo ou pode seguir as instruções em Baixar o aplicativo para baixar o exemplo e, em seguida, executar o projeto QueueResults.

Pontos de Interesse

As linhas de informação 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
    
  • A pendingWork tarefa está Nothing no início apenas FinishOneGroupAsync para o grupo A, que começou primeiro. O Grupo A ainda não completou uma expressão de espera quando chegar.FinishOneGroupAsync Portanto, o controle não retornou ao AccessTheWebAsync, e 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 e StartButton_Click atribuir uma tarefa para o grupo ao pendingWork.

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

    Depois que um grupo entra StartButton_Clickno , a operação não conclui uma expressão de espera até que a operação entre .FinishOneGroupAsync Portanto, nenhuma outra operação pode obter controle durante esse segmento de código.

Rever e executar a aplicação de exemplo

Para entender melhor o aplicativo de exemplo, você pode baixá-lo, criá-lo você mesmo ou revisar o código no final deste tópico sem implementar o aplicativo.

Nota

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

Transferir a aplicação

  1. Baixe o arquivo compactado de Async Samples: Reentrancy in .NET Desktop Apps.

  2. Descompacte o arquivo que você baixou e, em seguida, 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 para o projeto que você deseja executar e escolha Definir como StartUpProject.

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

Criando 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 Visual Basic e, em seguida, expanda Windows.

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

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

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

  6. No Editor de Códigos 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 View Code.

  7. No modo de 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 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 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. No 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, em seguida, escolha o botão Iniciar várias vezes.

  12. Faça as alterações em Desativar o botão Iniciar, Cancelar e reiniciar a operação ou Executar várias operações e enfileirar a saída para lidar com a reentrância.

Consulte também