Tratando a reentrada em aplicativos assíncronos (C# e Visual Basic)
Quando você incluir o código assíncrono em seu aplicativo, você deve considerar e possivelmente evitar o reentrada, que referencia reentering uma operação assíncrona antes de terminar.Se você não identifica e não trata possibilidades para o reentrada, pode causar resultados inesperados.
Neste tópico
Observação |
---|
As instruções em Examinando e executando o aplicativo de exemplo mostram como executar o código como um aplicativo Windows Presentation Foundation (WPF) ou como um aplicativo da Windows Store. Para executar o exemplo como um aplicativo WPF, você deve ter o Visual Studio 2012 O Visual Studio 2012 express para a área de trabalho do Windows, ou o .NET Framework 4.5 instalado no seu computador. Para executar o exemplo como um aplicativo de Windows Store , você deve ter o Windows 8 instalado no seu computador.Além disso, se deseja executar o exemplo do Visual Studio, também deverá ter o Visual Studio 2012 ou o Visual Studio Express 2012 for Windows 8 instalado. |
Reconhecendo Reentrada
No exemplo neste tópico, os usuários escolhem um botão de Iniciar para iniciar um aplicativo assíncrono que baixa uma série de sites e calculam o número de bytes total que é baixado.Uma versão síncrono de exemplo poderia responder a mesma maneira independentemente de quantas vezes um usuário escolher o botão porque, após a primeira vez, o encadeamento de interface do usuário ignora os eventos até que o aplicativo termina de executar.Em um aplicativo assíncrono, no entanto, o encadeamento de interface do usuário continua a responder, e você pode digitar novamente a operação assíncrona antes de terminar.
O exemplo a seguir mostra a saída previstas se o usuário escolher o botão de Iniciar apenas uma vez.Uma lista de sites baixadas aparece com o tamanho, em bytes, de cada site.O número de bytes total aparece no final.
1. msdn.microsoft.com/en-us/library/hh191443.aspx 83732
2. msdn.microsoft.com/en-us/library/aa578028.aspx 205273
3. msdn.microsoft.com/en-us/library/jj155761.aspx 29019
4. msdn.microsoft.com/en-us/library/hh290140.aspx 117152
5. msdn.microsoft.com/en-us/library/hh524395.aspx 68959
6. msdn.microsoft.com/en-us/library/ms404677.aspx 197325
7. msdn.microsoft.com 42972
8. msdn.microsoft.com/en-us/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 é chamado repetidamente, e o processo de download reentered cada vez.Como resultado, várias operações assíncronas estão executando ao mesmo tempo, as intercalações de saída os resultados, e o número de bytes total é confuso.
1. msdn.microsoft.com/en-us/library/hh191443.aspx 83732
2. msdn.microsoft.com/en-us/library/aa578028.aspx 205273
3. msdn.microsoft.com/en-us/library/jj155761.aspx 29019
4. msdn.microsoft.com/en-us/library/hh290140.aspx 117152
5. msdn.microsoft.com/en-us/library/hh524395.aspx 68959
1. msdn.microsoft.com/en-us/library/hh191443.aspx 83732
2. msdn.microsoft.com/en-us/library/aa578028.aspx 205273
6. msdn.microsoft.com/en-us/library/ms404677.aspx 197325
3. msdn.microsoft.com/en-us/library/jj155761.aspx 29019
7. msdn.microsoft.com 42972
4. msdn.microsoft.com/en-us/library/hh290140.aspx 117152
8. msdn.microsoft.com/en-us/library/ff730837.aspx 146159
TOTAL bytes returned: 890591
5. msdn.microsoft.com/en-us/library/hh524395.aspx 68959
1. msdn.microsoft.com/en-us/library/hh191443.aspx 83732
2. msdn.microsoft.com/en-us/library/aa578028.aspx 205273
6. msdn.microsoft.com/en-us/library/ms404677.aspx 197325
3. msdn.microsoft.com/en-us/library/jj155761.aspx 29019
4. msdn.microsoft.com/en-us/library/hh290140.aspx 117152
7. msdn.microsoft.com 42972
5. msdn.microsoft.com/en-us/library/hh524395.aspx 68959
8. msdn.microsoft.com/en-us/library/ff730837.aspx 146159
TOTAL bytes returned: 890591
6. msdn.microsoft.com/en-us/library/ms404677.aspx 197325
7. msdn.microsoft.com 42972
8. msdn.microsoft.com/en-us/library/ff730837.aspx 146159
TOTAL bytes returned: 890591
Você pode examinar o código que produz saída essas enrolando ao final deste tópico.Você pode fazer experiências com o código fazendo a solução seu computador local e então executando o projeto de WebsiteDownload ou usando o código no final deste tópico para criar seu próprio projeto para mais informações e instruções, consulte Examinando e executando o aplicativo de exemplo.
Tratamento Reentrada
Você pode manipular o reentrada em uma variedade de maneiras, dependendo do que você deseja que seu aplicativo para fazer.Este tópico apresenta os exemplos seguintes:
-
Desativar o botão de Iniciar quando executar a operação de modo que o usuário não possa a quebrar.
Cancelar a operação e reinicie
Cancelar qualquer operação que ainda executar quando o usuário escolher o botão de Iniciar novamente, e deixar a operação recentemente solicitada continuar.
Executar várias operações e coloque a saída
Permite que todas as operações executem solicitadas de forma assíncrona, mas coordenar a exibição de saída de modo que os resultados de cada operação aparecem juntos e em ordem.
Desativar o botão Iniciar
Você pode bloquear o botão de Iniciar quando uma operação executar desativando o botão na parte superior do manipulador de eventos StartButton_Click .Você pode então reativar o botão de dentro de um bloco de finally quando concluir a operação de modo que os usuários podem 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 no código no final deste tópico, ou você pode baixar o aplicativo concluído de Exemplos de Async: Reentrada em aplicativos desktop .NET ou Exemplos de Async: Reentrada em aplicativos da Windows Store.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
private async void StartButton_Click(object sender, RoutedEventArgs e)
{
// 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 (Exception)
{
ResultsTextBox.Text += "\r\nDownloads failed.";
}
// ***Enable the Start button in case you want to run the program again.
finally
{
StartButton.IsEnabled = true;
}
}
Como resultado de alterações, o botão não responde quando AccessTheWebAsync baixar sites, o processo não pode ser reentered.
Cancelar a operação e reinicie
Em vez de desativar o botão de Iniciar , você pode manter o botão ativa mas, se o usuário escolher o botão novamente, cancela a operação que já está em execução e está deixando a operação recentemente iniciada continuar.
Para obter mais informações sobre o botão, consulte Ajustando seu aplicativo de Async.
Para configurar esse cenário, faça as seguintes alterações no código básico que é fornecido em Examinando e executando o aplicativo de exemplo.Você também pode baixar o aplicativo concluído de Exemplos de Async: Reentrada em aplicativos desktop .NET ou Exemplos de Async: Reentrada em aplicativos da Windows Store.O nome do projeto é CancelAndRestart.
Declarar uma variável de CancellationTokenSource , cts, que está no escopo para todos os métodos.
Class MainWindow // Or Class MainPage ' *** Declare a System.Threading.CancellationTokenSource. Dim cts As CancellationTokenSource
public partial class MainWindow : Window // Or class MainPage { // *** Declare a System.Threading.CancellationTokenSource. CancellationTokenSource cts;
Em StartButton_Click, determina se uma operação já está em andamento.Se o valor de cts é zero (Nothing no Visual Basic), nenhuma operação já está ativa.Se o valor não for zero, a operação que já está executando será cancelada.
' *** If a download process is already underway, cancel it. If cts IsNot Nothing Then cts.Cancel() End If
// *** If a download process is already underway, cancel it. if (cts != null) { cts.Cancel(); }
Definir cts a 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
// *** Now set cts to a new value that you can use to cancel the current process // if the button is chosen again. CancellationTokenSource newCTS = new CancellationTokenSource(); cts = newCTS;
No final de StartButton_Click, o processo atual está completo, então definir o valor de cts de volta para o zero.
' *** When the process completes, signal that another process can proceed. If cts Is newCTS Then cts = Nothing End If
// *** When the process is complete, signal that another process can begin. if (cts == newCTS) cts = null;
O código a seguir mostra todas as alterações em StartButton_Click.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
private async void StartButton_Click(object sender, RoutedEventArgs e)
{
// This line is commented out to make the results clearer in the output.
//ResultsTextBox.Clear();
// *** If a download process is already underway, cancel it.
if (cts != null)
{
cts.Cancel();
}
// *** Now set cts to cancel the current process if the button is chosen again.
CancellationTokenSource newCTS = new CancellationTokenSource();
cts = newCTS;
try
{
// ***Send cts.Token to carry the message if there is a cancellation request.
await AccessTheWebAsync(cts.Token);
}
// *** Catch cancellations separately.
catch (OperationCanceledException)
{
ResultsTextBox.Text += "\r\nDownloads canceled.\r\n";
}
catch (Exception)
{
ResultsTextBox.Text += "\r\nDownloads failed.\r\n";
}
// *** When the process is complete, signal that another process can proceed.
if (cts == newCTS)
cts = null;
}
Em AccessTheWebAsync, faça as seguintes alterações.
Adicionar um parâmetro para aceitar o símbolo de cancelamento de StartButton_Click.
Use o método de GetAsync para baixar sites porque GetAsync aceita um argumento de CancellationToken .
Antes de chamar DisplayResults para exibir os resultados para cada site baixada, verifique ct para verificar que a operação atual não seja 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
// *** Provide a parameter for the CancellationToken from StartButton_Click.
async Task AccessTheWebAsync(CancellationToken ct)
{
// Declare an HttpClient object.
HttpClient client = new HttpClient();
// Make a list of web addresses.
List<string> urlList = SetUpURLList();
var total = 0;
var position = 0;
foreach (var url in urlList)
{
// *** Use the HttpClient.GetAsync method because it accepts a
// cancellation token.
HttpResponseMessage response = await client.GetAsync(url, ct);
// *** Retrieve the website contents from the HttpResponseMessage.
byte[] urlContents = await response.Content.ReadAsByteArrayAsync();
// *** Check for cancellations before displaying information about the
// latest site.
ct.ThrowIfCancellationRequested();
DisplayResults(url, urlContents, ++position);
// Update the total.
total += urlContents.Length;
}
// Display the total count for all of the websites.
ResultsTextBox.Text +=
string.Format("\r\n\r\nTOTAL bytes returned: {0}\r\n", total);
}
Se você escolher o botão de Iniciar várias vezes quando o aplicativo for executado, deve gerar os resultados parecidas com a seguinte saída.
1. msdn.microsoft.com/en-us/library/hh191443.aspx 83732
2. msdn.microsoft.com/en-us/library/aa578028.aspx 205273
3. msdn.microsoft.com/en-us/library/jj155761.aspx 29019
4. msdn.microsoft.com/en-us/library/hh290140.aspx 122505
5. msdn.microsoft.com/en-us/library/hh524395.aspx 68959
6. msdn.microsoft.com/en-us/library/ms404677.aspx 197325
Download canceled.
1. msdn.microsoft.com/en-us/library/hh191443.aspx 83732
2. msdn.microsoft.com/en-us/library/aa578028.aspx 205273
3. msdn.microsoft.com/en-us/library/jj155761.aspx 29019
Download canceled.
1. msdn.microsoft.com/en-us/library/hh191443.aspx 83732
2. msdn.microsoft.com/en-us/library/aa578028.aspx 205273
3. msdn.microsoft.com/en-us/library/jj155761.aspx 29019
4. msdn.microsoft.com/en-us/library/hh290140.aspx 117152
5. msdn.microsoft.com/en-us/library/hh524395.aspx 68959
6. msdn.microsoft.com/en-us/library/ms404677.aspx 197325
7. msdn.microsoft.com 42972
8. msdn.microsoft.com/en-us/library/ff730837.aspx 146159
TOTAL bytes returned: 890591
Para eliminar as listas parciais, tire comentários a primeira linha de código em StartButton_Click para desmarcar a caixa de texto sempre que o usuário reinicia a operação.
Executar várias operações e coloque a saída
Este exemplo é o terceiro mais complicado que o aplicativo é iniciado outra operação assíncrona sempre que o usuário escolha o botão de Iniciar , e todas as operações executadas para a conclusão.Todas as operações solicitadas baixar sites da lista de forma assíncrona, mas saída das operações são apresentadas em seqüência.Ou seja, a atividade real de transferência é intercalada, porque a saída em Reconhecendo Reentrada mostram, mas a lista de resultados para cada grupo é apresentada separadamente.
Operações compartilham Taskglobal, pendingWork, que serve como um porteiro para o processo de exibição.
Você pode executar este exemplo às alterações no código em Compilando o aplicativo, ou você pode seguir as instruções em Fazendo o download do aplicativo para baixar o exemplo e então executar o projeto de QueueResults.
A saída a seguir mostra o resultado se o usuário escolher o botão de Iniciar apenas uma vez.O rótulo da letra, A, indica que o resultado é o botão de Iniciar é escolhido a primeira vez.Números mostra a ordem de URL na lista de destinos de download.
#Starting group A.
#Task assigned for group A.
A-1. msdn.microsoft.com/en-us/library/hh191443.aspx 87389
A-2. msdn.microsoft.com/en-us/library/aa578028.aspx 209858
A-3. msdn.microsoft.com/en-us/library/jj155761.aspx 30870
A-4. msdn.microsoft.com/en-us/library/hh290140.aspx 119027
A-5. msdn.microsoft.com/en-us/library/hh524395.aspx 71260
A-6. msdn.microsoft.com/en-us/library/ms404677.aspx 199186
A-7. msdn.microsoft.com 53266
A-8. msdn.microsoft.com/en-us/library/ff730837.aspx 148020
TOTAL bytes returned: 918876
#Group A is complete.
Se o usuário escolher o botão de Iniciar três vezes, o aplicativo gera saída parecidas com as seguintes linhas.As linhas de informações que começam com um rastreamento de sustenido (#) o andamento do aplicativo.
#Starting group A.
#Task assigned for group A.
A-1. msdn.microsoft.com/en-us/library/hh191443.aspx 87389
A-2. msdn.microsoft.com/en-us/library/aa578028.aspx 207089
A-3. msdn.microsoft.com/en-us/library/jj155761.aspx 30870
A-4. msdn.microsoft.com/en-us/library/hh290140.aspx 119027
A-5. msdn.microsoft.com/en-us/library/hh524395.aspx 71259
A-6. msdn.microsoft.com/en-us/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/en-us/library/ff730837.aspx 148010
TOTAL bytes returned: 916095
B-1. msdn.microsoft.com/en-us/library/hh191443.aspx 87389
B-2. msdn.microsoft.com/en-us/library/aa578028.aspx 207089
B-3. msdn.microsoft.com/en-us/library/jj155761.aspx 30870
B-4. msdn.microsoft.com/en-us/library/hh290140.aspx 119027
B-5. msdn.microsoft.com/en-us/library/hh524395.aspx 71260
B-6. msdn.microsoft.com/en-us/library/ms404677.aspx 199186
#Group A is complete.
B-7. msdn.microsoft.com 53266
B-8. msdn.microsoft.com/en-us/library/ff730837.aspx 148010
TOTAL bytes returned: 916097
C-1. msdn.microsoft.com/en-us/library/hh191443.aspx 87389
C-2. msdn.microsoft.com/en-us/library/aa578028.aspx 207089
#Group B is complete.
C-3. msdn.microsoft.com/en-us/library/jj155761.aspx 30870
C-4. msdn.microsoft.com/en-us/library/hh290140.aspx 119027
C-5. msdn.microsoft.com/en-us/library/hh524395.aspx 72765
C-6. msdn.microsoft.com/en-us/library/ms404677.aspx 199186
C-7. msdn.microsoft.com 56190
C-8. msdn.microsoft.com/en-us/library/ff730837.aspx 148010
TOTAL bytes returned: 920526
#Group C is complete.
O início de grupos B e o C antes que o grupo Para concluir, mas saída para cada grupo aparece separadamente.Todas as saídas para o grupo Como aparecem primeiro, seguido por todas as saídas para o grupo B e em seguida, todas as saídas para o grupo C.O aplicativo sempre exibe os grupos em ordem, e para cada grupo, sempre exibe informações sobre sites individuais na ordem que as urls aparecem na lista URL.
No entanto, você não pode prever a ordem em que downloads estão realmente.Depois que vários grupos foram enfiados, todas as tarefas de download que geram são ativos.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 da-2.
Definições globais
O exemplo de código contém as duas declarações globais que são visíveis para 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)
public partial class MainWindow : Window // Class MainPage in Windows Store app.
{
// ***Declare the following variables where all methods can access them.
private Task pendingWork = null;
private char group = (char)('A' - 1);
A variável de Task , pendingWork, vigia o processo de exibição e impede qualquer grupo interrompa a operação de exibição de outro grupo.A variável de caracteres, group, rotula a saída de grupos diferentes para verificar que os resultados apareçam na ordem esperado.
O manipulador de eventos de clique
O manipulador de eventos, StartButton_Click, incrementos a letra do grupo sempre que o usuário escolha o botão de Iniciar .No manipulador chama AccessTheWebAsync para executar a operação de transferência.
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
private async void StartButton_Click(object sender, RoutedEventArgs e)
{
// ***Verify that each group's results are displayed together, and that
// the groups display in order, by marking each group with a letter.
group = (char)(group + 1);
ResultsTextBox.Text += string.Format("\r\n\r\n#Starting group {0}.", group);
try
{
// *** Pass the group value to AccessTheWebAsync.
char finishedGroup = await AccessTheWebAsync(group);
// The following line verifies a successful return from the download and
// display procedures.
ResultsTextBox.Text += string.Format("\r\n\r\n#Group {0} is complete.\r\n", finishedGroup);
}
catch (Exception)
{
ResultsTextBox.Text += "\r\nDownloads failed.";
}
}
O método de AccessTheWebAsync
Este exemplo AccessTheWebAsync divide em dois métodos.O primeiro método, AccessTheWebAsync, ele começa todas as tarefas de download para um grupo e definir - acima de pendingWork para controlar o processo de exibição.O método usa uma consulta consulta integrada a linguagem (LINQ) e ToArray<TSource> para iniciar qualquer download tarefas ao mesmo tempo.
AccessTheWebAsync então chama FinishOneGroupAsync para aguardar conclusão de cada download e exibir seu comprimento.
FinishOneGroupAsync retorna uma tarefa que é atribuído a pendingWork em AccessTheWebAsync.O valor que impede a interrupção por outra operação antes que a tarefa estiver 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
private async Task<char> AccessTheWebAsync(char grp)
{
HttpClient client = new HttpClient();
// Make a list of the web addresses to download.
List<string> urlList = SetUpURLList();
// ***Kick off the downloads. The application of ToArray activates all the download tasks.
Task<byte[]>[] getContentTasks = urlList.Select(url => client.GetByteArrayAsync(url)).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("\r\n#Task assigned for group {0}. Download tasks are active.\r\n", 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;
}
O método de FinishOneGroupAsync
Este método fornece um ciclo com as tarefas de download em um grupo, aguardando cada um, exibindo o comprimento do site e baixada, adicionando o comprimento total para o.
A primeira instrução em FinishOneGroupAsync usa pendingWork para certificar-se que inserir o método não interfere com uma operação que já estão em processo de exibição ou que já está esperando.Se uma operação está em andamento, a operação inserindo deve aguardar sua volta.
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
private async Task FinishOneGroupAsync(List<string> urls, Task<byte[]>[] contentTasks, char grp)
{
// ***Wait for the previous group to finish displaying results.
if (pendingWork != null) await pendingWork;
int total = 0;
// contentTasks is the array of Tasks that was created in AccessTheWebAsync.
for (int i = 0; i < contentTasks.Length; i++)
{
// Await the download of a particular URL, and then display the URL and
// its length.
byte[] content = await contentTasks[i];
DisplayResults(urls[i], content, i, grp);
total += content.Length;
}
// Display the total count for all of the websites.
ResultsTextBox.Text +=
string.Format("\r\n\r\nTOTAL bytes returned: {0}\r\n", total);
}
Você pode executar este exemplo arrastando as alterações no código em Compilando o aplicativo, ou você pode seguir as instruções em Fazendo o download do aplicativo para baixar o exemplo, e então executar o projeto de QueueResults.
Pontos de interesse
As linhas de informações que começam com um sustenido (#) na saída de como este exemplo funciona.
A saída mostra os seguintes padrões.
Um grupo pode ser iniciado quando um grupo anterior exibir sua saída, mas a exibição de saída anteriores do grupo não é interrompida.
#Starting group A. #Task assigned for group A. Download tasks are active. A-1. msdn.microsoft.com/en-us/library/hh191443.aspx 87389 A-2. msdn.microsoft.com/en-us/library/aa578028.aspx 207089 A-3. msdn.microsoft.com/en-us/library/jj155761.aspx 30870 A-4. msdn.microsoft.com/en-us/library/hh290140.aspx 119037 A-5. msdn.microsoft.com/en-us/library/hh524395.aspx 71260 #Starting group B. #Task assigned for group B. Download tasks are active. A-6. msdn.microsoft.com/en-us/library/ms404677.aspx 199186 A-7. msdn.microsoft.com 53078 A-8. msdn.microsoft.com/en-us/library/ff730837.aspx 148010 TOTAL bytes returned: 915919 B-1. msdn.microsoft.com/en-us/library/hh191443.aspx 87388 B-2. msdn.microsoft.com/en-us/library/aa578028.aspx 207089 B-3. msdn.microsoft.com/en-us/library/jj155761.aspx 30870 #Group A is complete. B-4. msdn.microsoft.com/en-us/library/hh290140.aspx 119027 B-5. msdn.microsoft.com/en-us/library/hh524395.aspx 71260 B-6. msdn.microsoft.com/en-us/library/ms404677.aspx 199186 B-7. msdn.microsoft.com 53078 B-8. msdn.microsoft.com/en-us/library/ff730837.aspx 148010 TOTAL bytes returned: 915908
A tarefa de pendingWork éNothing nula (no Visual Basic) no início de FinishOneGroupAsync somente para o grupo de A, que iniciou primeiro.Para agrupar ainda não tiver concluído uma expressão de espera quando ele atinge FinishOneGroupAsync.Como consequência, o controle não retornou a AccessTheWebAsync, e a primeira atribuição a pendingWork não ocorreu.
As duas linhas sempre aparecem juntos na saída.O código é desfeito nunca entre iniciar a operação de um grupo em StartButton_Click e atribuir uma tarefa do grupo da pendingWork.
#Starting group B. #Task assigned for group B. Download tasks are active.
Depois que um grupo entra em StartButton_Click, a operação não concluir uma expressão de espera até que a operação entre em FinishOneGroupAsync.Como consequência, nenhuma outra operação pode obter o controle durante o segmento de código.
Examinando e executando o aplicativo de exemplo
Para compreender melhor o aplicativo de exemplo, você poderá baixá-lo, compilá-lo você mesmo, ou examinar o código no final deste tópico sem implementar o aplicativo.
Observação |
---|
Para executar o exemplo como um aplicativo da área de trabalho do Windows Presentation Foundation (WPF), você deve ter o Visual Studio 2012 O Visual Studio express library 2012 para a área de trabalho do Windows, ou .NET Framework 4.5 instalado no seu computador. Para executar o exemplo como um aplicativo de Windows Store , você deve ter o Windows 8 instalado no seu computador.Além disso, se deseja executar o exemplo do Visual Studio, também deverá ter o Visual Studio 2012 ou o Visual Studio Express 2012 for Windows 8 instalado.O Visual Studio 2010 não pode carregar os projetos que são destinados para o .NET Framework 4.5. |
Fazendo o download do aplicativo
Baixar o arquivo compactado de Exemplos de Async: Reentrada em aplicativos desktop .NET ou Exemplos de Async: Reentrada em aplicativos da Windows Store.
Descompactar o arquivo que você baixou, e comece em Visual Studio.
Na barra de menu, 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).
Em Gerenciador de Soluções, abra o menu de atalho para o projeto que você deseja executar, e então escolha Defina como StartUpProject.
Escolha as teclas CTRL + f5 para compilar e executar o projeto.
Compilando o aplicativo
As seções a seguir fornecem código para criar o exemplo como um aplicativo WPF ou como um aplicativo de Windows Store .
Para criar um aplicativo WPF
Inicie o Visual Studio 2012.
Na barra de menu, escolha Arquivo, Novo, Projeto.
A Caixa de diálogo Novo Projeto é exibida.
No painel de Modelos Instalados , expanda Visual Basic ou **Visual C#**em seguida, expanda Janelas.
Na lista de tipos de projeto, escolha Aplicativo WPF.
Nomeie o projeto WebsiteDownloadWPF, e então escolha o botão de 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 em Gerenciador de Soluções, e escolha Exibir Código.
No modo de XAML de MainWindow.xaml, substitua o código com o código a seguir.
<Window x:Class="MainWindow" xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:WebsiteDownloadWPF" xmlns:d="https://schemas.microsoft.com/expression/blend/2008" xmlns:mc="https://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>
<Window x:Class="WebsiteDownloadWPF.MainWindow" xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:WebsiteDownloadWPF" xmlns:d="https://schemas.microsoft.com/expression/blend/2008" xmlns:mc="https://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 Design de MainWindow.xaml.
Adicione uma referência para System.Net.Http.
Em Gerenciador de Soluções, abra o menu de atalho para MainWindow.xaml.vb ou MainWindow.xaml.cs, escolha Exibir Código.
Em MainWindow.xaml.vb ou em MainWindow.xaml.cs, substitua o código com o 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) ' 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/en-us/library/hh191443.aspx", "https://msdn.microsoft.com/en-us/library/aa578028.aspx", "https://msdn.microsoft.com/en-us/library/jj155761.aspx", "https://msdn.microsoft.com/en-us/library/hh290140.aspx", "https://msdn.microsoft.com/en-us/library/hh524395.aspx", "https://msdn.microsoft.com/en-us/library/ms404677.aspx", "https://msdn.microsoft.com", "https://msdn.microsoft.com/en-us/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("http://", "") ' 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
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; // Add the following using directives, and add a reference for System.Net.Http. using System.Net.Http; using System.Threading; namespace WebsiteDownloadWPF { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private async void StartButton_Click(object sender, RoutedEventArgs e) { // This line is commented out to make the results clearer in the output. //ResultsTextBox.Text = ""; try { await AccessTheWebAsync(); } catch (Exception) { ResultsTextBox.Text += "\r\nDownloads failed."; } } private async Task AccessTheWebAsync() { // Declare an HttpClient object. HttpClient client = new HttpClient(); // Make a list of web addresses. List<string> urlList = SetUpURLList(); var total = 0; var position = 0; foreach (var url in urlList) { // GetByteArrayAsync returns a task. At completion, the task // produces a byte array. byte[] urlContents = await client.GetByteArrayAsync(url); DisplayResults(url, urlContents, ++position); // Update the total. total += urlContents.Length; } // Display the total count for all of the websites. ResultsTextBox.Text += string.Format("\r\n\r\nTOTAL bytes returned: {0}\r\n", total); } private List<string> SetUpURLList() { List<string> urls = new List<string> { "https://msdn.microsoft.com/en-us/library/hh191443.aspx", "https://msdn.microsoft.com/en-us/library/aa578028.aspx", "https://msdn.microsoft.com/en-us/library/jj155761.aspx", "https://msdn.microsoft.com/en-us/library/hh290140.aspx", "https://msdn.microsoft.com/en-us/library/hh524395.aspx", "https://msdn.microsoft.com/en-us/library/ms404677.aspx", "https://msdn.microsoft.com", "https://msdn.microsoft.com/en-us/library/ff730837.aspx" }; return urls; } private void DisplayResults(string url, byte[] content, int pos) { // 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://". var displayURL = url.Replace("http://", ""); // Display position in the URL list, the URL, and the number of bytes. ResultsTextBox.Text += string.Format("\n{0}. {1,-58} {2,8}", pos, displayURL, content.Length); } } }
Escolha as teclas CTRL + f5 para executar o programa, e então escolha o botão de Iniciar várias vezes.
Faça as alterações de Desativar o botão Iniciar, Cancelar a operação e reinicie, ou de Executar várias operações e coloque a saída para manipular o reentrada.
Para criar um aplicativo da Windows Store
Inicie o Visual Studio 2012.
Na barra de menu, escolha Arquivo, Novo, Projeto.
A Caixa de diálogo Novo Projeto é exibida.
Em Instalado, a categoria de Modelos , expanda Visual Basic ou Visual C#, e expanda Windows Store.
Na lista de tipos de projeto, escolha Aplicativo em branco (XAML).
Nomeie o projeto WebsiteDownloadWin, e então escolha o botão de OK .
O novo projeto aparece no Gerenciador de Soluções.
Em Gerenciador de Soluções, abra o menu de atalho para MainPage.xaml, escolha Abrir.
Na janela de XAML de MainPage.xaml, substitua o código com o código a seguir.
<Page x:Class="WebsiteDownloadWin.MainPage" xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:WebsiteDownloadWin" xmlns:d="https://schemas.microsoft.com/expression/blend/2008" xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" FontSize="12"> <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}"> <Button x:Name="StartButton" Content="Start" HorizontalAlignment="Left" Margin="325,77,0,0" VerticalAlignment="Top" Click="StartButton_Click" Height="145" Background="#FFA89B9B" FontSize="36" Width="711" /> <TextBox x:Name="ResultsTextBox" HorizontalAlignment="Left" Margin="325,222,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="546" FontSize="10" ScrollViewer.VerticalScrollBarVisibility="Visible" Width="711" FontFamily="Lucida Console" /> </Grid> </Page>
Uma janela simples que contém uma caixa de texto e um botão de Iniciar aparece na janela de Design de MainPage.xaml.
Em Gerenciador de Soluções, abra o menu de atalho para MainPage.xaml.vb ou MainPage.xaml.cs, escolha Exibir Código.
Substitua o código em MainPage.xaml.vb ou em MainPage.xaml.cs com o código a seguir.
' Add the following Imports statements. Imports System.Threading.Tasks Imports System.Threading Imports System.Net.Http Public NotInheritable Class MainPage Inherits Page Protected Overrides Sub OnNavigatedTo(e As Navigation.NavigationEventArgs) End Sub 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 = "" 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/en-us/library/hh191443.aspx", "https://msdn.microsoft.com/en-us/library/aa578028.aspx", "https://msdn.microsoft.com/en-us/library/jj155761.aspx", "https://msdn.microsoft.com/en-us/library/hh290140.aspx", "https://msdn.microsoft.com/en-us/library/hh524395.aspx", "https://msdn.microsoft.com/en-us/library/ms404677.aspx", "https://msdn.microsoft.com", "https://msdn.microsoft.com/en-us/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("http://", "") ' 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
using System; using System.Collections.Generic; using System.IO; using System.Linq; using Windows.Foundation; using Windows.Foundation.Collections; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls.Primitives; using Windows.UI.Xaml.Data; using Windows.UI.Xaml.Input; using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Navigation; // Add the following using directives. using System.Threading.Tasks; using System.Threading; using System.Net.Http; namespace WebsiteDownloadWin { public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); } private async void StartButton_Click(object sender, RoutedEventArgs e) { // This line is commented out to make the results clearer in the output. //ResultsTextBox.Text = ""; try { await AccessTheWebAsync(); } catch (Exception) { ResultsTextBox.Text += "\r\nDownloads failed."; } } private async Task AccessTheWebAsync() { // Declare an HttpClient object. HttpClient client = new HttpClient(); // Make a list of web addresses. List<string> urlList = SetUpURLList(); var total = 0; var position = 0; foreach (var url in urlList) { // GetByteArrayAsync returns a task. At completion, the task // produces a byte array. byte[] urlContents = await client.GetByteArrayAsync(url); DisplayResults(url, urlContents, ++position); // Update the total. total += urlContents.Length; } // Display the total count for all of the websites. ResultsTextBox.Text += string.Format("\r\n\r\nTOTAL bytes returned: {0}\r\n", total); } private List<string> SetUpURLList() { List<string> urls = new List<string> { "https://msdn.microsoft.com/en-us/library/hh191443.aspx", "https://msdn.microsoft.com/en-us/library/aa578028.aspx", "https://msdn.microsoft.com/en-us/library/jj155761.aspx", "https://msdn.microsoft.com/en-us/library/hh290140.aspx", "https://msdn.microsoft.com/en-us/library/hh524395.aspx", "https://msdn.microsoft.com/en-us/library/ms404677.aspx", "https://msdn.microsoft.com", "https://msdn.microsoft.com/en-us/library/ff730837.aspx" }; return urls; } private void DisplayResults(string url, byte[] content, int pos) { // 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://". var displayURL = url.Replace("http://", ""); // Display position in the URL list, the URL, and the number of bytes. ResultsTextBox.Text += string.Format("\n{0}. {1,-58} {2,8}", pos, displayURL, content.Length); } } }
Escolha as teclas CTRL + f5 para executar o programa, e então escolha o botão de Iniciar várias vezes.
Faça as alterações de Desativar o botão Iniciar, Cancelar a operação e reinicie, ou de Executar várias operações e coloque a saída para manipular o reentrada.
Consulte também
Tarefas
Passo a passo: Acessando a Web usando Async e aguardar (C# e Visual Basic)
Conceitos
Programação com Async assíncrona e esperar (C# e Visual Basic)