Podpora vícenásobného přístupu v aplikacích s modifikátorem Async (C# a Visual Basic)
Pokud zahrnete asynchronní kód do vaší aplikace, měli byste vzít v úvahu a případně zabránit vícenásobnému přístupu, který se vztahuje k nutnosti opětovného zadávání asynchronní operace ještě před dokončením.Pokud neidentifikujete a nemanipulujete s možnostmi vícenásobnému přístupu, může způsobit neočekávané výsledky.
V tomto tématu
[!POZNÁMKA]
Pokyny v části Revize a spuštění vzorové aplikace ukazují, jak spustit kód jako aplikaci WPF nebo jako aplikaci Windows Store.
Chcete-li spustit příklad jako aplikaci WPF, musíte mít nainstalovanou aplikaci Visual Studio 2012, Visual Studio 2013, Visual Studio Express 2012 for Windows Desktop, Visual Studio Express 2013 for Windows nebo rozhraní .NET Framework 4.5 nebo 4.5.1 v počítači.
Chcete-li spustit příklad jako aplikaci Windows Store, musíte mít v počítači nainstalován systém Windows 8.Kromě toho pokud chcete spustit příklad ze sady Visual Studio, musíte také mít nainstalováno Visual Studio 2012 Visual Studio 2013, Visual Studio Express 2012 pro Windows Desktop nebo Visual Studio Express 2013 for Windows.
Rozpoznávání vícenásobného přístupu
V příkladu v tomto tématu uživatelé vyberou tlačítko Start pro zahájení asynchronní aplikace, která stáhne řady webů a vypočítá celkový počet bajtů, které byly staženy.Synchronní verze tohoto příkladu odpoví stejným způsobem bez ohledu na to, kolikrát uživatel vybere tlačítko, protože poprvé vlákno uživatelského rozhraní bude tyto události ignorovat až do ukončení aplikace.V asynchronní aplikaci však vlákno UI i nadále odpovídá a můžete znovu zadat asynchronní operaci před dokončením.
Následující příklad zobrazuje očekávaný výstup, pokud uživatel klikne na tlačítko Start pouze jednou.Seznam stažených webových stránek se zobrazuje s velikostí v bajtech pro každý web.Na konci se zobrazuje celkový počet bajtů.
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
Klikne-li však uživatel na tlačítko více než jednou, obslužná rutina události je vyvolána opakovaně, a proces stahování je znovu zadán pokaždé.V důsledku toho je několik asynchronních operací spuštěno současně. Výstup předřadí výsledky a celkový počet bajtů je matoucí.
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
Můžete zkontrolovat kód, který vytváří tento výstup, přechodem na konec tohoto tématu.Můžete experimentovat s kódem stažením řešení do místního počítače a následným spuštěním projektu WebsiteDownload nebo pomocí kódu na konci tohoto tématu k vytvoření vlastního projektu: Další informace a pokyny naleznete v tématu Revize a spuštění vzorové aplikace.
Vyřešení vícenásobného přístupu
Můžete zpracovat vícenásobnost různými způsoby podle toho, co má vaše aplikace provést.Toto téma představuje následující příklady:
-
Zakáže tlačítko Start během operace tak, aby ji uživatel nemohl přerušit.
Zrušení a opětovné spuštění operace
Zrušit jakoukoli operaci, která je stále spuštěna, když uživatel vybere tlačítko Start znovu a umožnit pokračování nedávno požadované operace.
Spuštění více operací a zařazení výstupu do fronty
Povolit, aby všechny požadované operace běžely asynchronně, ale koordinovat zobrazení výstupu tak, aby se výsledky z každé operace zobrazovaly společně a v daném pořadí.
Zakázání tlačítka Start
Můžete blokovat tlačítko Start, kdy je spuštěná operace, zakázáním tlačítka v horní části obslužné rutiny události StartButton_Click.Potom můžete znovu povolit tlačítko z bloku finally po ukončení operace tak, aby uživatelé mohli znovu spustit aplikaci.
Následující kód znázorňuje tyto změny, které jsou označeny hvězdičkami.Změny můžete přidat do kódu na konci tohoto tématu nebo si můžete stáhnout hotovou aplikaci z Asynchronní vzorky: Vícenásobnost v desktopových aplikacích .NET nebo Asynchronní vzorky: Vícenásobnost v aplikacích pro Windows Store.Název projektu je 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;
}
}
V důsledku změny, tlačítko nereaguje zatímco AccessTheWebAsync stahuje webové stránky, takže proces nelze znovu zadat.
Zrušení a opětovné spuštění operace
Namísto zakázání tlačítka Start můžete zachovat tlačítko aktivní, ale pokud uživatel na toto tlačítko znovu klepne, zrušit operaci, která je již spuštěna a nechá pokračovat operaci naposledy spuštěnou.
Další informace o zrušení naleznete v tématu Jemné vyladění asynchronní aplikace.
Pokud chcete nastavit tento scénář, proveďte následující změny základního kódu, který je k dispozici v tématu Revize a spuštění vzorové aplikace.Dokončenou aplikaci můžete také stáhnout z okna Asynchronní příklady: Opětovný vstup do desktopových aplikací .NET Desktop nebo synchronní příklady: Opětovný vstup do aplikací pro Windows Store.Název tohoto projektu je CancelAndRestart.
Deklarujte proměnnou CancellationTokenSource, cts, která je v oboru pro všechny metody.
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;
V StartButton_Click určete, zda operace je již probíhá.Pokud hodnota cts má hodnotu null (Nothing v jazyce Visual Basic), žádná operace ještě není aktivní.Pokud hodnota není null, je zrušena operace, která je již spuštěna.
' *** 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(); }
Nastavit cts na jinou hodnotu, která představuje aktuální proces.
' *** 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;
Na konci StartButton_Click je aktuální proces dokončen, takže nastavte hodnotu cts zpět na hodnotu null.
' *** 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;
Následující kód ukazuje všechny změny v StartButton_Click.Přidání jsou označena hvězdičkami.
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;
}
Proveďte následující změny v AccessTheWebAsync.
Přidat parametr pro přijetí tokenu zrušení z StartButton_Click.
Metodu GetAsync použijte ke stažení webových stránek, protože GetAsync přijímá argument CancellationToken.
Před voláním DisplayResults pro zobrazení výsledků pro každý stažený web, zkontrolujte ct k ověření, že aktuální operace nebyla zrušena.
Následující kód znázorňuje tyto změny, které jsou označeny hvězdičkami.
' *** 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);
}
Zvolíte-li tlačítko Start několikrát během spuštění této aplikace, mohou být vráceny výsledky, které se podobají následujícímu výstupu.
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
Chcete-li odstranit dílčí seznamy, zrušte komentář u prvního řádku kódu v StartButton_Click pro vymazání textového pole pokaždé, když uživatel znovu spustí operaci.
Spuštění více operací a zařazení výstupu do fronty
Tento třetí příklad je v nejkomplikovanější v tomu, že aplikace spustí jinou asynchronní operaci pokaždé, když uživatel vybere tlačítko Start a všechny operace běží až do ukončení.Všechny požadované operace stahují webové stránky ze seznamu asynchronně, ale výstup z operací je uveden postupně.To znamená, že skutečná aktivita stahování je prokládána, jak poukazuje výstup Rozpoznávání vícenásobného přístupu, ale seznam výsledků pro každou skupinu je předkládán samostatně.
Operace sdílí globální Task, pendingWork, který slouží jako správce pro zpracování zobrazení.
Tento příklad lze spustit zadáním nebo vložením změn do kódu v článku Sestavení aplikace nebo můžete postupovat podle pokynů v tématu Stažení aplikace ke stažení vzorku a spuštění projektu QueueResults.
Následující výstup zobrazuje výsledek toho, když uživatel klikne na tlačítko Start pouze jednou.Označení písmenem A označuje, že výsledek pochází z prvního stisku tlačítka Start.Čísla popisují pořadí adres URL v seznamu cílů ke stažení.
#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.
Pokud uživatel klikne na tlačítko Start třikrát, aplikace vytvoří výstup, který se podobá následujícím řádkům.Informační řádky, které začínají znakem křížku (#), sledují průběh aplikace.
#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.
Skupiny B a C se spustí před dokončením skupiny A, ale výstup pro jednotlivé skupiny se zobrazí odděleně.Nejprve se zobrazí výstup pro skupinu A, následovaný všemi výstupy pro skupinu B a pak veškerý výstup pro skupinu C.Aplikace vždy zobrazí skupiny v pořadí a pro každou skupinu vždy zobrazí informace o jednotlivých webech v pořadí, ve kterém se adresy URL nachází v seznamu adres URL.
Nelze však předvídat pořadí, ve kterých ke stažení skutečně došlo.Po spuštění více skupin budou aktivní všechny úlohy stahování, které generují.Nelze předpokládat, že A-1 se stáhne před B-1 a nelze předpokládat, že A-1 se stáhne před A-2.
Globální definice
Ukázka kódu obsahuje následující dvě globální deklarace, které jsou viditelné ze všech metod.
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);
Proměnná Task, pendingWork, dohlíží na proces zobrazení a zabrání jakékoli skupině v přerušení operace zobrazení jiné skupiny.Proměnné znaku group, značí výstup z různých skupin, pokud chcete ověřit, že se výsledky zobrazí v očekávaném pořadí.
Obslužná rutina události kliknutí
Obslužná rutina události StartButton_Click zvyšuje písmeno skupiny pokaždé, když uživatel klikne na tlačítko Start.Potom rutina volá AccessTheWebAsync ke spuštění operace stahování.
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.";
}
}
Metoda AccessTheWebAsync
V tomto příkladu se rozdělí AccessTheWebAsync do dvou metod.První metoda AccessTheWebAsync spustí všechny úlohy stahování pro skupinu a nastaví pendingWork pro řízení procesu zobrazení.Metoda se používá dotaz LINQ a ToArray``1 pro zahájení současného stahování všech úloh.
AccessTheWebAsync pak zavolá FinishOneGroupAsync pro čekání na dokončení každého stažení a zobrazení jeho délky.
FinishOneGroupAsync vrátí úkol, který je přiřazen k pendingWork v AccessTheWebAsync.Tato hodnota zabraňuje přerušení jinou operací před dokončením úkolu.
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;
}
Metoda FinishOneGroupAsync
Tato metoda projde stažené úlohy ve skupině a čeká na jednotlivé z nich při současném zobrazení délky staženého webu a přičtením délky k celku.
První příkaz v FinishOneGroupAsync používá pendingWork k tomu, aby se ujistil, že zadávání metody není v konfliktu s operací, která je již v procesu zobrazení, nebo která již čeká.Pokud tato operace probíhá, operace zadávání musí čekat, než přijde na řadu.
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);
}
Tento příklad lze spustit zadáním nebo vložením změn do kódu v článku Sestavení aplikace nebo můžete postupovat podle pokynů v tématu Stažení aplikace ke stažení vzorku a spuštění projektu QueueResults.
Body zájmu
Informační řádky, které začínají znakem křížku (#) ve výstupu značí, jak tento příklad funguje.
Výstup zobrazuje následující vzory.
Skupina může být spuštěna, když předchozí skupina zobrazuje výstup, ale zobrazení výstupu předchozí skupiny není přerušeno.
#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
Úkol pendingWork má hodnotu null (Nothing v jazyce Visual Basic) na začátku FinishOneGroupAsync pouze u skupiny A, která začíná jako první.Skupina A dosud nedokončila výraz await, když dosáhl FinishOneGroupAsync.Proto se ovládací prvek nevrátil do AccessTheWebAsync a první přiřazení k pendingWork se neuskutečnilo.
Následující dva řádky se ve výstupu vždy zobrazí společně.Kód není nikdy přerušen mezi spuštěním operace skupiny StartButton_Click a přiřazením úkolu skupiny pro pendingWork.
#Starting group B. #Task assigned for group B. Download tasks are active.
Po vstupu skupiny do StartButton_Click, operace nedokončí výraz await, dokud operace nevstoupí do FinishOneGroupAsync.Proto žádná další operace nemůže získat kontrolu během tohoto segmentu kódu.
Prostudování a spuštění ukázkové aplikace
Pro lepší pochopení příkladu aplikace si ho můžete stáhnout, sestavit sami nebo zkontrolovat kód na konci tohoto tématu bez implementace aplikace.
[!POZNÁMKA]
Chcete-li spustit příklad jako aplikaci pro stolní počítače Windows Presentation Foundation (WPF), musíte mít nainstalovanou aplikaci Visual Studio 2012, Visual Studio 2013, Visual Studio Express 2012 for Windows Desktop, Visual Studio Express 2013 for Windows nebo rozhraní .NET Framework 4.5 nebo 4.5.1 v počítači.
Chcete-li spustit příklad jako aplikaci Windows Store, musíte mít v počítači nainstalován systém Windows 8.Kromě toho, pokud chcete spustit příklad ze sady Visual Studio, musíte také mít nainstalováno Visual Studio 2012 Visual Studio 2013, Visual Studio Express 2012 pro Windows 8 nebo Visual Studio Express 2013 for Windows.Projekty sady Visual Studio 2010 nemohou načítat projekty určené pro rozhraní .NET Framework 4.5.
Stažení aplikace
Stažení komprimovaného souboru z Asynchronní vzorky: vícenásobný přístup v aplikacích .NET pro počítač nebo Asynchronní vzorky: vícenásobný přístup v aplikacích Windows Store.
Dekomprimujte soubor, který jste stáhli a poté spusťte Visual Studio.
Na panelu nabídky vyberte možnosti Soubor, Otevřít, Projekt/řešení.
Přejděte do složky obsahující dekomprimovaný ukázkový kód a poté otevřete soubor řešení (.sln).
V Průzkumníku řešení otevřete místní nabídku projektu, který chcete spustit, a poté vyberte položku Nastavit jako výchozí projekt.
Výběrem tlačítek CTRL+F5 sestavte a spusťte projekt.
Vytvoření aplikace
Následující části obsahují kód k vytvoření příkladu jako aplikace WPF nebo aplikace Windows Store.
Vytvoření aplikace WPF
Spusťte aplikaci Visual Studio.
Na panelu nabídky vyberte možnosti Soubor, Nový, Projekt.
Otevře se dialogové okno Nový projekt.
V podokně Nainstalované šablony rozbalte možnost Visual Basic nebo Visual C# a potom rozbalte možnost Windows.
V seznamu typy projektů, klepněte na příkaz Aplikace WPF.
Pojmenujte projekt WebsiteDownloadWPF a stiskněte tlačítko OK.
V podokně Průzkumník řešení se zobrazí nový projekt.
V editoru kódu sady Visual Studio zvolte kartu MainWindow.xaml.
Pokud karta není zobrazena, otevřete místní nabídku souboru MainWindow.xaml v Průzkumníkovi řešení a vyberte možnost Zobrazit kód.
V zobrazení XAML pro souboru MainWindow.xaml nahraďte kód následujícím kódem.
<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>
Jednoduché okno obsahující textové pole a tlačítko se zobrazí v zobrazení Návrh souboru MainWindow.xaml.
Přidejte odkaz pro System.Net.Http.
V Průzkumníku řešení otevřete místní nabídku pro soubor MainWindow.xaml.vb nebo MainWindow.xaml.cs a pak zvolte možnost Zobrazit kód.
Nahraďte kód v souborech MainWindow.xaml.vb nebo MainWindow.xaml.cs za následující kód.
' 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); } } }
Zvolte klávesy CTRL+ F5 ke spuštění programu a pak několikrát vyberte tlačítko Start.
Proveďte požadované změny z nabídky Zakázat tlačítko Start, Zrušit a znovu spustit operaci nebo Spustit více operací a vložit výstup do fronty pro zpracování vícenásobného přístupu.
Vytvoření aplikace pro Windows Store
Spusťte aplikaci Visual Studio.
Na panelu nabídky vyberte možnosti Soubor, Nový, Projekt.
Otevře se dialogové okno Nový projekt.
V kategorii Nainstalováno, Šablony rozbalte možnost Visual Basic nebo Visual C# a pak rozbalte možnost Windows Store.
V seznamu typy projektů, klepněte na příkaz Prázdná aplikace (XAML).
Pojmenujte projekt WebsiteDownloadWin a stiskněte tlačítko OK.
V podokně Průzkumník řešení se zobrazí nový projekt.
V Průzkumníku řešení otevřete místní nabídku pro MainPage.xaml a pak zvolte Otevřít.
V okně XAML pro soubor MainPage.xaml nahraďte kód následujícím kódem.
<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>
Jednoduché okno obsahující textové pole a tlačítko Start se zobrazí v okně Návrh souboru MainPage.xaml.
V Průzkumníku řešení otevřete místní nabídku pro soubor MainPage.xaml nebo MainPage.xaml.cs a pak zvolte možnost Zobrazit kód.
Nahraďte kód pro MainPage.xaml.vb nebo MainPage.xaml.cs za následující kód.
' 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); } } }
Zvolte klávesy CTRL+ F5 ke spuštění programu a pak několikrát vyberte tlačítko Start.
Proveďte požadované změny z nabídky Zakázat tlačítko Start, Zrušit a znovu spustit operaci nebo Spustit více operací a vložit výstup do fronty pro zpracování vícenásobného přístupu.
Viz také
Úkoly
Návod: Přístup k webu pomocí modifikátoru Async a operátoru Await (C# a Visual Basic)
Koncepty
Asynchronní programování pomocí modifikátoru Async a operátoru Await (C# a Visual Basic)
Další zdroje
Asynchronní programování (aplikace pro Windows Store)
Rychlý začátek: volání asynchronních rozhraní API v jazyce C# nebo Visual Basic