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 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.
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
Em
StartButton_Click
, determine se uma operação já está em andamento. Se o valor dects
éNothing
, nenhuma operação já está ativa. Se o valor nãoNothing
for , 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
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
No final do
StartButton_Click
, o processo atual está concluído, portanto, defina o valor dects
volta paraNothing
.' *** 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, verifiquect
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, pendingWork
que 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.
FinishOneGroupAsync
Retorna 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 apenasFinishOneGroupAsync
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 aoAccessTheWebAsync
, e a primeira atribuição apendingWork
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 aopendingWork
.#Starting group B. #Task assigned for group B. Download tasks are active.
Depois que um grupo entra
StartButton_Click
no , 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
Baixe o arquivo compactado de Async Samples: Reentrancy in .NET Desktop Apps.
Descompacte o arquivo que você baixou e, em seguida, inicie o Visual Studio.
Na barra de menus, escolha Arquivo, Abrir, Projeto/Solução.
Navegue até a pasta que contém o código de exemplo descompactado e abra o arquivo de solução (.sln).
No Gerenciador de Soluções, abra o menu de atalho para o projeto que você deseja executar e escolha Definir como StartUpProject.
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
Inicie o Visual Studio.
Na barra de menus, escolha Arquivo, Novo, Projeto.
A caixa de diálogo Novo projeto é aberta.
No painel Modelos Instalados, expanda Visual Basic e, em seguida, expanda Windows.
Na lista de tipos de projeto, escolha Aplicativo WPF.
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.
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.
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.
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.
No Gerenciador de Soluções, abra o menu de atalho para MainWindow.xaml.vb e escolha Exibir Código.
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
Escolha as teclas CTRL+F5 para executar o programa e, em seguida, escolha o botão Iniciar várias vezes.
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.