Поделиться через


WhenAny. Связывание .NET Framework и среды выполнения Windows (C# и Visual Basic)

Пример в этом разделе объединяет тип Среда выполнения Windows, загружающий веб-каналы блога асинхронно с методом .NET Framework, обрабатывающим асинхронные задачи в порядке выполнения. Дополнительные сведения о типе см. в разделе SyndicationClient. Дополнительные сведения о методе см. в разделе Task.WhenAny.

Объединив эти функции, можно начать загрузку нескольких веб-каналов блогов одновременно и обрабатывать результаты по мере их завершения. Если один веб-канал загрузился быстрее, чем другие, его результаты отображаются в первую очередь. С помощью метода SyndicationClient загружать веб-каналы проще; с помощью метода Task.WhenAny проще определять следующий канал, загрузка которого завершена.

Примечание

Для выполнения примера на компьютере должен быть установлен Windows 8.Кроме того, если требуется запустить пример из Visual Studio, следует установить Visual Studio 2012, Visual Studio 2013 Visual Studio Express 2012 для Windows 8, Visual Studio Express 2013 для Windows.

Следующий код объединяет эти функции из Среда выполнения Windows и .NET Framework.

  • SyndicationClient.RetrieveFeedAsync загружает веб-каналы блога и возвращает экземпляры IAsyncOperationWithProgress.

  • Метод расширения AsTask``2 представляет экземпляры IAsyncOperationWithProgress в экземпляры Task.

  • WhenAny``1 возвращает задачу, которая определяет первую задачу в коллекции задач для выполнения.

Try 
    Dim feedsQuery As IEnumerable(Of Task(Of SyndicationFeed)) =
        From uri In uriList
        Select client.RetrieveFeedAsync(uri).AsTask()
    ' AsTask changes the returns from RetrieveFeedAsync into tasks. 

    ' Run the query to start all the asynchronous processes. 
    Dim blogFeedTasksList As List(Of Task(Of SyndicationFeed)) = feedsQuery.ToList()

    Dim feed As SyndicationFeed

    ' Repeat the following until there are no tasks left: 
    '    - Grab the first one that finishes. 
    '    - Retrieve the results from the task (what the return statement  
    '      in RetrieveFeedAsync returns). 
    '    - Remove the task from the list. 
    '    - Display the results. 
    While blogFeedTasksList.Count > 0
        Dim nextTask As Task(Of SyndicationFeed) = Await Task.WhenAny(blogFeedTasksList)
        feed = Await nextTask
        blogFeedTasksList.Remove(nextTask)
        DisplayResults(feed)
    End While 

Catch ex As Exception
    ResultsTextBox.Text =
        "Page could not be loaded." & vbCrLf & "Exception: " & ex.ToString()
End Try
try
{
    IEnumerable<Task<SyndicationFeed>> feedsQuery =
            from uri in uriList
            // AsTask changes the returns from RetrieveFeedAsync into tasks. 
            select client.RetrieveFeedAsync(uri).AsTask();

    // Run the query to start all the asynchronous processes.
    List<Task<SyndicationFeed>> blogFeedTasksList = feedsQuery.ToList();

    SyndicationFeed feed;

    // Repeat the following until no tasks remain: 
    //    - Grab the first one that finishes. 
    //    - Retrieve the results from the task (what the return statement  
    //      in RetrieveFeedAsync returns). 
    //    - Remove the task from the list. 
    //    - Display the results. 
    while (blogFeedTasksList.Count > 0)
    {
        Task<SyndicationFeed> nextTask = await Task.WhenAny(blogFeedTasksList);
        feed = await nextTask;                    
        blogFeedTasksList.Remove(nextTask);
        DisplayResults(feed);
    }
}
catch (Exception ex)
{
    ResultsTextBox.Text =
        "Page could not be loaded.\n\r" + "Exception: " + ex.ToString();
}

В предыдущем примере получался результат, аналогичный следующим строкам. Для каждого блога отображается заголовок блога, а затем заголовки и даты записей блога.

Developing for Windows
     New blog for Windows 8 app developers, 5/1/2012 2:33:02 PM -07:00
     Trigger-Start Services Recipe, 3/24/2011 2:23:01 PM -07:00
     . . .
     Countdown to PDC10, 10/26/2010 4:11:28 PM -07:00

Extreme Windows Blog
     PDXLAN 20: “Epidemic” Custom PC by Jon Hansz, 7/30/2012 2:31:35 PM -07:00
     Samsung Notebook Series 9: Taking Thin and Light to the Extreme, 7/23/2012 12:06:03 PM -07:00
     . . .
     AMD Unveils A-Series APUs, 6/13/2011 9:34:01 PM -07:00

Blogging Windows
     Windows 8 has reached the RTM milestone, 8/1/2012 9:00:00 AM -07:00
     Windows 8 will be available on…, 7/18/2012 1:09:00 PM -07:00
     . . .
     More buzz from BUILD – Developers get their devices!, 9/13/2011 7:47:57 PM -07:00

Springboard Series Blog
     What to Expect in User Experience Virtualization Beta 2, 6/25/2012 11:03:27 PM -07:00
     Introducing Microsoft BitLocker Administration 2.0 Beta, 6/12/2012 8:08:23 AM -07:00
     . . .
     The Springboard Series Visits Lima, Peru, 11/18/2011 5:27:37 AM -08:00

Оставшаяся часть этого раздела содержит сведения о создании примера и принципы ее работы.

Для запуска этого приложения на компьютере должны быть установлены Visual Studio 2012 и Windows 8.

В этом разделе содержатся следующие подразделы.

  • Параметры установки для примера
  • Общие сведения о начальном коде
  • Расширение начального кода
  • Загрузка начального кода
  • Загрузка завершенного приложения
  • Построение начального кода
  • Построение завершенного приложения
  • Связанные разделы

Параметры установки для примера

Пример основан на читателе блога, описанном в Краткое руководство. Использование оператора await для асинхронного программирования. Однако код стартера этого раздела загрузил несколько веб-каналов блога вместо одного.

Начальный код использует функции Среда выполнения Windows для последовательной загрузки веб-каналов блога. То есть веб-канал блога загружены в том порядке, в котором они перечислены в коллекции URL-адреса. Завершенное приложение добавляет функцию из .NET Framework, чтобы загрузить веб-канал блога в порядке, в котором они завершаются.

Можно настроить пример кода любым из следующих способов:

  • Код начального объекта.

    • Можно загрузить код стартера самостоятельно, следуя инструкциям раздела Загрузка кода стартера.

    • Можно создать код стартера самостоятельно, следуя инструкциям раздела Создание кода стартера.

    • Просмотрите начальный код, не реализуя его, прокрутив до раздела Сборка начального кода.

  • Готовое приложение.

    • Можно загрузить готовое приложение, следуя инструкциям в разделе Загрузка готового приложения.

    • Можно собрать приложение самостоятельно, следуя инструкциям раздела Сборка завершенного приложения.

    • Можно просмотреть завершенное приложение без реализации его, перемещение в Сборка завершенного приложения.

Раздел Общие сведения о коде начального описывает ключевые моменты в соответствующем решении.

Раздел Расширение начального кода показывает, как можно изменить код путем добавления AsTask``2 и Task.WhenAny.

Общие сведения о начальном коде

Начальный код использует метод SyndicationClient, RetrieveFeedAsync, чтобы загрузить веб-канал блога из каждого URI в списке уникальных идентификаторов ресурса. Каждый вызов метода возвращает экземпляр IAsyncOperationWithProgress, который представляет выполняющуюся асинхронную операцию. Ожидалось, асинхронная операция создает SyndicationFeed экземпляр, содержащий сведения о загруженного канала блога.

Код определяет запрос, который применяет RetrieveFeedAsync в каждой записи в списке Ресурсов. То есть, запрос возвращает коллекцию экземпляров IAsyncOperationWithProgress.

Dim feedsQuery As IEnumerable(Of IAsyncOperationWithProgress(Of SyndicationFeed, 
                                                                RetrievalProgress)) =
                                                From uri In uriList
                                                Select client.RetrieveFeedAsync(uri)
IEnumerable<IAsyncOperationWithProgress<SyndicationFeed, 
    RetrievalProgress>> feedsQuery = from uri in uriList
                                     select client.RetrieveFeedAsync(uri);

ToList``1 выполняет запрос и запускает асинхронные процессы, как показано в следующем коде.

Dim blogFeedOpsList As List(Of IAsyncOperationWithProgress(Of SyndicationFeed, 
                                                           RetrievalProgress)) =
                                               feedsQuery.ToList()
List<IAsyncOperationWithProgress<SyndicationFeed, 
    RetrievalProgress>> blogFeedOpsList = feedsQuery.ToList();

На этом этапе у вас есть список активных экземпляров IAsyncOperationWithProgress. При этом следует ожидать каждого экземпляра для получения конечного результата.

Следующий цикл ожидает каждого экземпляра IAsyncOperationWithProgress для получения результатов SyndicationFeed.

Dim feed As SyndicationFeed
For Each blogFeedOp In blogFeedOpsList
    ' The Await operator retrieves the final result (a SyndicationFeed instance) 
    ' from each IAsyncOperation instance.
    feed = Await blogFeedOp
    DisplayResults(feed)
Next
SyndicationFeed feed;
foreach (var blogFeedOp in blogFeedOpsList)
{
    // The await operator retrieves the final result (a SyndicationFeed instance) 
    // from each IAsyncOperation instance.
    feed = await blogFeedOp;
    DisplayResults(feed);
}

Можно просмотреть эту версия программы в подразделе Сборка начального кода в конце раздела.

Дополнительные сведения о программировании с асинхронными Среда выполнения Windows API можно найти в кратком руководстве: использование оператора await для асинхронного программирования.

Расширение начального кода

Начальный код показывает, что SyndicationClient упрощает загрузку веб-каналов блога. Последний шаг для выполнения примера — разрешить приложению обработать веб-каналы блога в порядке выполнения их загрузок вместо порядка отображения их в списке URI.

Ключ для улучшения выполнения метода Task.WhenAny. При применении WhenAny к коллекции асинхронных процессов, метод возвращает первый процесс, который завершается, сводя к минимуму продолжительность ожидания. В этом примере порядок, в котором данные канала блога отображаются, не важен. Если одна загрузка проходит медленно, сначала отобразятся результаты из другого блога. Ситуация кажется совершенной для WhenAny, за исключением одного: метод WhenAny принимает коллекцию задач.

Вызов AsTask

WhenAny для коллекции Task или экземпляров Task, но метод SyndicationClient, который загружает веб-каналы блога, возвращает экземпляр IAsyncOperationWithProgress. Поэтому приложение мост между объектами IAsyncOperationWithProgress из Среда выполнения Windows и объектами Task из платформы .NET Framework.

.NET Framework предоставляет методы расширения AsTask``2, чтобы сделать переход. При вызове AsTask экземпляра IAsyncOperationWithProgress, AsTask возвращает задачу, которая представляет собой асинхронную операцию. Задача завершается, когда соответствующий экземпляр IAsyncOperationWithProgress завершается, а задача содержит результат или исключение экземпляра.

Поэтому нужно просто вызовите AsTask в каждом экземпляре IAsyncOperationWithProgress, RetrieveFeedAsync, как показано в следующем коде. Код переименовывает переменные, чтобы отразить изменения с задачами и использует явную типизацию для ясности.

Dim feedsQuery As IEnumerable(Of Task(Of SyndicationFeed)) =
    From uri In uriList
    Select client.RetrieveFeedAsync(uri).AsTask()
' AsTask changes the returns from RetrieveFeedAsync into tasks. 

' Run the query to start all the asynchronous processes. 
Dim blogFeedTasksList As List(Of Task(Of SyndicationFeed)) = feedsQuery.ToList()
IEnumerable<Task<SyndicationFeed>> feedsQuery =
        from uri in uriList
        // AsTask changes the returns from RetrieveFeedAsync into tasks. 
        select client.RetrieveFeedAsync(uri).AsTask();

// Run the query to start all the asynchronous processes.
List<Task<SyndicationFeed>> blogFeedTasksList = feedsQuery.ToList();

Примечание

AsTask играет важную роль в асинхронном программировании, о которой вы, возможно, не знаете.Компилятор использует AsTask при применении оператора await к экземпляру IAsyncAction или IAsyncOperation, как показано в следующем коде.

Применение WhenAny

Последний шаг в преобразовании — добавление метода Task.WhenAny в приложение. атрибут WhenAny применяется к коллекции задач (blogFeedTasksList) и возвращает первая задача в коллекции, которая завершается. Точнее, WhenAny возвращает задачу, которая, когда ее ожидают, оценивает задачу, которая завершилась первой.

В следующей выписка вызывает WhenAny и ожидает результата. Код использует явную типизацию для отображения результата более ясно.

Dim nextTask As Task(Of SyndicationFeed) = Await Task.WhenAny(blogFeedTasksList)
Task<SyndicationFeed> nextTask = await Task.WhenAny(blogFeedTasksList);

Следующий код выполняет те же действия, что и прежнее заявление, но делит операцию на два оператора, чтобы пояснить, что произошло. Первый оператор вызывает метод WhenAny, а второй ожидает результата.

' WhenAny returns a task that, when awaited, produces a task.
' Call:
Dim whenAnyTask As Task(Of Task(Of SyndicationFeed)) = Task.WhenAny(blogFeedTasksList)
' Await:
Dim nextTask As Task(Of SyndicationFeed) = Await whenAnyTask
// WhenAny returns a task that, when awaited, produces a task.
// Call:
Task<Task<SyndicationFeed>> whenAnyTask = Task.WhenAny(blogFeedTasksList);
// Await:
Task<SyndicationFeed> nextTask = await whenAnyTask;

Наконец, нужно ожидать nextTask для получения результатов (экземпляра SyndicationFeed ) из задачи, которая закончилась первой, а затем удалить nextTask из списка, чтобы не обрабатывать ее еще раз.

feed = Await nextTask
blogFeedTasksList.Remove(nextTask)
feed = await nextTask;                    
blogFeedTasksList.Remove(nextTask);

Используйте цикл while для выполнения этих шагов для каждой задачи в blogFeedTasksList.

While blogFeedTasksList.Count > 0
    Dim nextTask As Task(Of SyndicationFeed) = Await Task.WhenAny(blogFeedTasksList)
    feed = Await nextTask
    blogFeedTasksList.Remove(nextTask)
    DisplayResults(feed)
End While
while (blogFeedTasksList.Count > 0)
{
    Task<SyndicationFeed> nextTask = await Task.WhenAny(blogFeedTasksList);
    feed = await nextTask;                    
    blogFeedTasksList.Remove(nextTask);
    DisplayResults(feed);
}

Можно просмотреть эта версия программы в подразделе Сборка завершенного приложения в конце раздела. Или можно воспользоваться инструкциями в разделе Загрузка готового приложения для загрузки проекта.

Предупреждение

Использование WhenAny в цикле, как описано в примере, достаточно для проблем, которые включают небольшое число задач.Однако другие способы эффективнее, если имеется большое количество выполняемых задач.Дополнительные сведения и примеры см. в разделе Выполнение задач по мере завершения.

Загрузка начального кода

Можно загрузить код стартера для примера из Пример Async. Использование моста из .NET в Windows. Если отсутствует доступ в Интернет, следуйте инструкциям в разделе Создание начального кода в конце этого раздела.

После того как вы загрузили код, откройте и запустите его, выполнив следующие действия.

  1. Распакуйте загруженный файл и запустите Visual Studio 2012.

  2. В строке меню выберите Файл, Открыть, Проект/Решение.

  3. Перейдите к папке, содержащей распакованный образец кода, откройте файл решения (sln) для AsTaskWhenAnyDemoVB или AsTaskWhenAnyDemoCS.

  4. В Обозревателе решений откройте контекстное меню для проекта SequentialBlogReader и выберите Назначить запускаемым проектом.

  5. Нажмите клавишу F5, чтобы собрать и запустить проект.

  6. Запустите его несколько раз, чтобы убедиться, что результаты отображаются в том же порядке каждый раз.

Просмотрите файл MainPage.xaml.vb или MainPage.xaml.cs в подразделе Сборка начального кода в конце раздела.

Пример основан на читателе блога, описанном в Краткое руководство. Использование оператора await для асинхронного программирования. Однако код стартера этого раздела загрузил несколько веб-каналов блога вместо одного.

Дополнительные сведения о разнообразии усовершенствований и расширений, которые можно внести в приложение, см. раздел Создание средства чтения блога.

Загрузка завершенного приложения

Если вручную не требуется собирать образец, можно загрузить полный пример. Следуйте инструкциям в разделе Загрузка начального кода, однако выберите WhenAnyBlogReader как Запускаемый проект.

Запустите программу несколько раз, чтобы проверить, что веб-канал блога отображаются в различные заказы.

Просмотрите файл MainPage.xaml.vb или MainPage.xaml.cs в подразделе Сборка завершенное приложение в конце раздела.

Построение начального кода

Можно загрузить образцы в этом разделе из Пример Async. Использование моста из .NET в Windows. Если вы предпочитаете установить приложение самостоятельно, выполните эти действия.

  1. Запустите Visual Studio 2012.

  2. В меню Файл выберите Создать, Проект.

    Откроется диалоговое окно Новый проект.

  3. В категории Установленные, Шаблоны выберите Visual Basic или Visual C# и выберите Магазин Windows в списке типов проектов.

  4. В списке типов проектов выберите Пустое приложение (XAML).

  5. Назовите проект SequentialBlogReader и нажмите кнопку ОК.

    В обозревателе решений появится новый проект.

  6. В Обозревателе решений откройте контекстное меню для MainPage.xaml и выберите Открыть.

  7. В окне XAML MainPage.xaml замените автоматически созданный код на следующий код.

    <Page
        x:Class="SequentialBlogReader.MainPage"
        xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:AsTaskWhenAnyDemo"
        xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
            <Button x:Name="StartButton" Content="Start" HorizontalAlignment="Stretch" Margin="325,128,330,0" VerticalAlignment="Top" Click="StartButton_Click" Height="71" Background="#FFA89B9B" FontWeight="Bold" FontSize="36"/>
            <TextBox x:Name="ResultsTextBox" Margin="325,222,330,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="546" FontSize="10" ScrollViewer.VerticalScrollBarVisibility="Visible" />
        </Grid>
    </Page>
    

    Простое окно, содержащее текстовое поле и кнопку, отображается в окне Конструктор MainPage.xaml.

    Дополнительные сведения о разнообразии усовершенствований и расширений, которые можно внести в ИП, см. раздел Создание средства чтения блога.

  8. В Обозревателе решений откройте контекстное меню для MainPage.xaml.vb или MainPage.xaml.cs и выберите Просмотреть код.

  9. В файле MainPage.xaml.vb или MainPage.xaml.cs замените код на следующий.

    ' Add an Imports statement for SyndicationClient. 
    Imports Windows.Web.Syndication
    
    
    ' The Blank Page item template is documented at http:'go.microsoft.com/fwlink/?LinkId=234238 
    
    Public NotInheritable Class MainPage
        Inherits Page
    
        Protected Overrides Sub OnNavigatedTo(e As Navigation.NavigationEventArgs)
    
        End Sub 
    
    
        ' The async modifier enables you to use await in the event handler. 
        Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)
            ResultsTextBox.Text = "" 
    
            ' Disable the button until the operation is complete.
            StartButton.IsEnabled = False 
    
            Dim client As Windows.Web.Syndication.SyndicationClient = New SyndicationClient()
    
            ' Force the SyndicationClient to download the information.
            client.BypassCacheOnRetrieve = True 
    
            Dim uriList = CreateUriList()
    
            Try 
                Dim feedsQuery As IEnumerable(Of IAsyncOperationWithProgress(Of SyndicationFeed, 
                                                                                RetrievalProgress)) =
                                                                From uri In uriList
                                                                Select client.RetrieveFeedAsync(uri)
    
                ' Run the query to start all the asynchronous processes. 
                Dim blogFeedOpsList As List(Of IAsyncOperationWithProgress(Of SyndicationFeed, 
                                                                           RetrievalProgress)) =
                                                               feedsQuery.ToList()
    
                Dim feed As SyndicationFeed
                For Each blogFeedOp In blogFeedOpsList
                    ' The Await operator retrieves the final result (a SyndicationFeed instance) 
                    ' from each IAsyncOperation instance.
                    feed = Await blogFeedOp
                    DisplayResults(feed)
                Next 
    
            Catch ex As Exception
                ResultsTextBox.Text =
                    "Page could not be loaded." & vbCrLf & "Exception: " & ex.ToString()
            End Try 
    
            ' Reenable the button in case you want to run the operation again.
            StartButton.IsEnabled = True 
        End Sub 
    
    
        Function CreateUriList() As List(Of Uri)
    
            ' Create a list of URIs. 
            Dim uriList = New List(Of Uri) From
            {
                    New Uri("https://windowsteamblog.com/windows/b/developers/atom.aspx"),
                    New Uri("https://windowsteamblog.com/windows/b/extremewindows/atom.aspx"),
                    New Uri("https://windowsteamblog.com/windows/b/bloggingwindows/atom.aspx"),
                    New Uri("https://windowsteamblog.com/windows/b/springboard/atom.aspx")
            }
            Return uriList
        End Function 
    
    
        Sub DisplayResults(sf As SyndicationFeed)
    
            ' Title of the blog.
            ResultsTextBox.Text &= sf.Title.Text & vbCrLf
    
            ' Titles and dates for blog posts. 
            For Each item As SyndicationItem In sf.Items
    
                ResultsTextBox.Text &= vbTab & item.Title.Text & ", " &
                                    item.PublishedDate.ToString() & vbCrLf
            Next
    
            ResultsTextBox.Text &= vbCrLf
        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 a using directive for SyndicationClient. 
    using Windows.Web.Syndication;
    
    
    namespace SequentialBlogReader
    {
        public sealed partial class MainPage : Page
        {
            public MainPage()
            {
                this.InitializeComponent();
            }
    
            protected override void OnNavigatedTo(NavigationEventArgs e)
            {
            }
    
    
            private async void StartButton_Click(object sender, RoutedEventArgs e)
            {
                ResultsTextBox.Text = "";
    
                // Disable the button until the operation is complete.
                StartButton.IsEnabled = false;
    
                Windows.Web.Syndication.SyndicationClient client = new SyndicationClient();
    
                // Force the SyndicationClient to download the information.
                client.BypassCacheOnRetrieve = true;
    
                var uriList = CreateUriList();
    
                try
                {
                    IEnumerable<IAsyncOperationWithProgress<SyndicationFeed, 
                        RetrievalProgress>> feedsQuery = from uri in uriList
                                                         select client.RetrieveFeedAsync(uri);
    
                    // Run the query to start all the asynchronous processes.
                    List<IAsyncOperationWithProgress<SyndicationFeed, 
                        RetrievalProgress>> blogFeedOpsList = feedsQuery.ToList();
    
                    SyndicationFeed feed;
                    foreach (var blogFeedOp in blogFeedOpsList)
                    {
                        // The await operator retrieves the final result (a SyndicationFeed instance) 
                        // from each IAsyncOperation instance.
                        feed = await blogFeedOp;
                        DisplayResults(feed);
                    }
                }
                catch (Exception ex)
                {
                    ResultsTextBox.Text =
                        "Page could not be loaded.\n\r" + "Exception: " + ex.ToString();
                }
    
                // Reenable the button in case you want to run the operation again.
                StartButton.IsEnabled = true;
            }
    
            List<Uri> CreateUriList()
            {
                // Create a list of URIs.
                List<Uri> uriList = new List<Uri> 
                { 
                    new Uri("https://windowsteamblog.com/windows/b/developers/atom.aspx"),
                    new Uri("https://windowsteamblog.com/windows/b/extremewindows/atom.aspx"),
                    new Uri("https://windowsteamblog.com/windows/b/bloggingwindows/atom.aspx"),
                    new Uri("https://windowsteamblog.com/windows/b/springboard/atom.aspx")
                };
                return uriList;
            }
    
    
            void DisplayResults(SyndicationFeed sf)
            {
                // Title of the blog.
                ResultsTextBox.Text += sf.Title.Text + "\r\n";
    
                // Titles and dates for blog posts. 
                foreach (SyndicationItem item in sf.Items)
                {
                    ResultsTextBox.Text += "\t" + item.Title.Text + ", " +
                                        item.PublishedDate.ToString() + "\r\n";
                }
                ResultsTextBox.Text += "\r\n";
            }
        }
    }
    
  10. Нажмите клавишу F5, чтобы запустить программу, затем нажмите кнопку Start.

Построение завершенного приложения

Можно загрузить образцы в этом разделе из Пример Async. Использование моста из .NET в Windows. Если вы предпочитаете установить приложение самостоятельно, выполните эти действия.

  1. Запустите Visual Studio 2012.

  2. В меню Файл выберите Создать, Проект.

    Откроется диалоговое окно Новый проект.

  3. В категории Установленные, Шаблоны выберите Visual Basic или Visual C# и выберите Магазин Windows.

  4. В списке типов проектов выберите Пустое приложение (XAML).

  5. Назовите проект WhenAnyBlogReader и нажмите кнопку ОК.

    В обозревателе решений появится новый проект.

  6. В Обозревателе решений откройте контекстное меню для MainPage.xaml и выберите Открыть.

  7. В окне XAML MainPage.xaml замените автоматически созданный код на следующий код.

    <Page
        x:Class="WhenAnyBlogReader.MainPage"
        xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:AsTaskWhenAnyDemo"
        xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
            <Button x:Name="StartButton" Content="Start" HorizontalAlignment="Stretch" Margin="325,128,330,0" VerticalAlignment="Top" Click="StartButton_Click" Height="71" Background="#FFA89B9B" FontWeight="Bold" FontSize="36"/>
            <TextBox x:Name="ResultsTextBox" Margin="325,222,330,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="546" FontSize="10" ScrollViewer.VerticalScrollBarVisibility="Visible" />
        </Grid>
    </Page>
    

    Простое окно, содержащее текстовое поле и кнопку, отображается в окне Конструктор MainPage.xaml.

    Дополнительные сведения о разнообразии усовершенствований и расширений, которые можно внести в приложение, см. раздел Создание средства чтения блога.

  8. В Обозревателе решений откройте контекстное меню для MainPage.xaml.vb или MainPage.xaml.cs и выберите Просмотреть код.

  9. В файле MainPage.xaml.vb или MainPage.xaml.cs замените код на следующий.

    ' Add an Imports statement for SyndicationClient. 
    Imports Windows.Web.Syndication
    
    ' Add an Imports statement for the Tasks. 
    Imports System.Threading.Tasks
    
    ' The Blank Page item template is documented at http:'go.microsoft.com/fwlink/?LinkId=234238 
    
    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)
    
            ResultsTextBox.Text = "" 
    
            ' Disable the button until the operation is complete.
            StartButton.IsEnabled = False 
    
            Dim client As Windows.Web.Syndication.SyndicationClient = New SyndicationClient()
    
            ' Force the SyndicationClient to download the information.
            client.BypassCacheOnRetrieve = True 
    
            Dim uriList = CreateUriList()
    
            ' The following code avoids the use of implicit typing so that you  
            ' can see the types clearly. 
    
            Try 
                Dim feedsQuery As IEnumerable(Of Task(Of SyndicationFeed)) =
                    From uri In uriList
                    Select client.RetrieveFeedAsync(uri).AsTask()
                ' AsTask changes the returns from RetrieveFeedAsync into tasks. 
    
                ' Run the query to start all the asynchronous processes. 
                Dim blogFeedTasksList As List(Of Task(Of SyndicationFeed)) = feedsQuery.ToList()
    
                Dim feed As SyndicationFeed
    
                ' Repeat the following until there are no tasks left: 
                '    - Grab the first one that finishes. 
                '    - Retrieve the results from the task (what the return statement  
                '      in RetrieveFeedAsync returns). 
                '    - Remove the task from the list. 
                '    - Display the results. 
                While blogFeedTasksList.Count > 0
                    Dim nextTask As Task(Of SyndicationFeed) = Await Task.WhenAny(blogFeedTasksList)
                    feed = Await nextTask
                    blogFeedTasksList.Remove(nextTask)
                    DisplayResults(feed)
                End While 
    
            Catch ex As Exception
                ResultsTextBox.Text =
                    "Page could not be loaded." & vbCrLf & "Exception: " & ex.ToString()
            End Try 
    
            ' Reenable the button in case you want to run the operation again.
            StartButton.IsEnabled = True 
        End Sub 
    
    
        Function CreateUriList() As List(Of Uri)
    
            ' Create a list of URIs. 
            Dim uriList = New List(Of Uri) From
            {
                    New Uri("https://windowsteamblog.com/windows/b/developers/atom.aspx"),
                    New Uri("https://windowsteamblog.com/windows/b/extremewindows/atom.aspx"),
                    New Uri("https://windowsteamblog.com/windows/b/bloggingwindows/atom.aspx"),
                    New Uri("https://windowsteamblog.com/windows/b/springboard/atom.aspx")
            }
            Return uriList
        End Function 
    
    
        Sub DisplayResults(sf As SyndicationFeed)
    
            ' Title of the blog.
            ResultsTextBox.Text &= sf.Title.Text & vbCrLf
    
            ' Titles and dates for blog posts. 
            For Each item As SyndicationItem In sf.Items
    
                ResultsTextBox.Text &= vbTab & item.Title.Text & ", " &
                                    item.PublishedDate.ToString() & vbCrLf
            Next
    
            ResultsTextBox.Text &= vbCrLf
        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 a using directive for SyndicationClient. 
    using Windows.Web.Syndication;
    
    // Add a using directive for the Tasks. 
    using System.Threading.Tasks;
    
    
    namespace WhenAnyBlogReader
    {
        public sealed partial class MainPage : Page
        {
            public MainPage()
            {
                this.InitializeComponent();
            }
    
            protected override void OnNavigatedTo(NavigationEventArgs e)
            {
            }
    
    
            private async void StartButton_Click(object sender, RoutedEventArgs e)
            {
                ResultsTextBox.Text = "";
    
                // Disable the button until the operation is complete.
                StartButton.IsEnabled = false;
    
                Windows.Web.Syndication.SyndicationClient client = new SyndicationClient();
    
                // Force the SyndicationClient to download the information.
                client.BypassCacheOnRetrieve = true;
    
                var uriList = CreateUriList();
    
                // The following code avoids the use of implicit typing (var) so that you  
                // can identify the types clearly. 
    
                try
                {
                    IEnumerable<Task<SyndicationFeed>> feedsQuery =
                            from uri in uriList
                            // AsTask changes the returns from RetrieveFeedAsync into tasks. 
                            select client.RetrieveFeedAsync(uri).AsTask();
    
                    // Run the query to start all the asynchronous processes.
                    List<Task<SyndicationFeed>> blogFeedTasksList = feedsQuery.ToList();
    
                    SyndicationFeed feed;
    
                    // Repeat the following until no tasks remain: 
                    //    - Grab the first one that finishes. 
                    //    - Retrieve the results from the task (what the return statement  
                    //      in RetrieveFeedAsync returns). 
                    //    - Remove the task from the list. 
                    //    - Display the results. 
                    while (blogFeedTasksList.Count > 0)
                    {
                        Task<SyndicationFeed> nextTask = await Task.WhenAny(blogFeedTasksList);
                        feed = await nextTask;                    
                        blogFeedTasksList.Remove(nextTask);
                        DisplayResults(feed);
                    }
                }
                catch (Exception ex)
                {
                    ResultsTextBox.Text =
                        "Page could not be loaded.\n\r" + "Exception: " + ex.ToString();
                }
    
                // Reenable the button in case you want to run the operation again.
                StartButton.IsEnabled = true;
            }
    
    
            List<Uri> CreateUriList()
            {
                // Create a list of URIs.
                List<Uri> uriList = new List<Uri> 
                { 
                    new Uri("https://windowsteamblog.com/windows/b/developers/atom.aspx"),
                    new Uri("https://windowsteamblog.com/windows/b/extremewindows/atom.aspx"),
                    new Uri("https://windowsteamblog.com/windows/b/bloggingwindows/atom.aspx"),
                    new Uri("https://windowsteamblog.com/windows/b/springboard/atom.aspx")
                };
                return uriList;
            }
    
    
            void DisplayResults(SyndicationFeed sf)
            {
                // Title of the blog.
                ResultsTextBox.Text += sf.Title.Text + "\r\n";
    
                // Titles and dates for blog posts. 
                foreach (SyndicationItem item in sf.Items)
                {
                    ResultsTextBox.Text += "\t" + item.Title.Text + ", " +
                                        item.PublishedDate.ToString() + "\r\n";
                }
                ResultsTextBox.Text += "\r\n";
            }
        }
    }
    
  10. Нажмите клавишу F5, чтобы запустить программу, затем нажмите кнопку Start.

См. также

Ссылки

AsTask``1

WhenAny``1

Основные понятия

Асинхронное программирование с использованием ключевых слов Async и Await (C# и Visual Basic)

Отмена оставшихся асинхронных задач после завершения одной из них (C# и Visual Basic)

Запуск нескольких асинхронных задач и их обработка по мере завершения (C# и Visual Basic)

Другие ресурсы

Краткое руководство. Использование оператора await для асинхронного программирования

Создание средства чтения блога

IAsyncOperationWithProgress