异步取消:.NET Framework 和 Windows 运行时之间的桥接(C# 和 Visual Basic)

您可通过组合 .NET Framework 和 Windows 运行时 的功能来最大化您的资源。 本主题中的示例演示如何使用 .NET Framework CancellationToken 实例将取消按钮添加到使用 Windows 运行时 方法从 Web 下载博客源的应用程序。

备注

若要运行示例,您必须在计算机上安装 Windows 8。此外,如要从 Visual Studio 运行示例,还必须安装 Visual Studio 2012、Visual Studio 2013、Visual Studio express 2012 for Windows 8 或Visual Studio Express 2013 for Windows。

AsTask 提供桥

取消标记需要 Task 实例,但 Windows 运行时 方法生成 IAsyncOperationWithProgress 实例。 您可使用 .NET Framework 中的 AsTask``2 扩展方法在它们之间充当桥梁。

示例中的 DownloadBlogsAsync 方法完成了大部分工作。

Async Function DownloadBlogsAsync(ct As CancellationToken) As Task
    Dim client As Windows.Web.Syndication.SyndicationClient = New SyndicationClient()

    Dim uriList = CreateUriList()

    ' Force the SyndicationClient to download the information.
    client.BypassCacheOnRetrieve = True 


    ' The following code avoids the use of implicit typing (var) so that you  
    ' can identify the types clearly. 

    For Each uri In uriList
        ' ***These three lines are combined in the single statement that follows them. 
        'Dim feedOp As IAsyncOperationWithProgress(Of SyndicationFeed, RetrievalProgress) = 
        '    client.RetrieveFeedAsync(uri) 
        'Dim feedTask As Task(Of SyndicationFeed) = feedOp.AsTask(ct) 
        'Dim feed As SyndicationFeed = Await feedTask 

        ' ***You can combine the previous three steps in one expression. 
        Dim feed As SyndicationFeed = Await client.RetrieveFeedAsync(uri).AsTask(ct)

        DisplayResults(feed, ct)
    Next 
End Function
async Task DownloadBlogsAsync(CancellationToken ct)
{
    Windows.Web.Syndication.SyndicationClient client = new SyndicationClient();

    var uriList = CreateUriList();

    // Force the SyndicationClient to download the information.
    client.BypassCacheOnRetrieve = true;

    // The following code avoids the use of implicit typing (var) so that you  
    // can identify the types clearly. 

    foreach (var uri in uriList)
    {
        // ***These three lines are combined in the single statement that follows them. 
        //IAsyncOperationWithProgress<SyndicationFeed, RetrievalProgress> feedOp =  
        //    client.RetrieveFeedAsync(uri); 
        //Task<SyndicationFeed> feedTask = feedOp.AsTask(ct); 
        //SyndicationFeed feed = await feedTask; 

        // ***You can combine the previous three steps in one expression.
        SyndicationFeed feed = await client.RetrieveFeedAsync(uri).AsTask(ct);

        DisplayResults(feed);
    }
}

循环的注释部分显示了详细的过渡步骤。

  • 从在指定的 URI 下载博客源的异步操作开始,调用 SyndicationClient.RetrieveFeedAsync。 异步操作是 IAsyncOperationWithProgress 实例。

    Dim feedOp As IAsyncOperationWithProgress(Of SyndicationFeed, RetrievalProgress) = 
        client.RetrieveFeedAsync(uri)
    
    IAsyncOperationWithProgress<SyndicationFeed, RetrievalProgress> feedOp =  
        client.RetrieveFeedAsync(uri);
    
  • 因为您想使用的 .NET Framework 中的取消功能要求任务,代码应用 AsTask``1 去将 IAsyncOperationWithProgress 实例显示为 Task。 特别是,代码将应用接受 CancellationToken 参数的 AsTask 重载。

    Dim feedTask As Task(Of SyndicationFeed) = feedOp.AsTask(ct)
    
    Task<SyndicationFeed> feedTask = feedOp.AsTask(ct);
    
  • 最后,await 或 Await 运算符等待任务检索 SyndicationFeed 结果。

    Dim feed As SyndicationFeed = Await feedTask
    
    SyndicationFeed feed = await feedTask;
    

有关 AsTask 的更多信息,请参阅 WhenAny:.NET Framework 和 Windows 运行时之间的桥接(C# 和 Visual Basic) 中的扩展起始代码

兴趣点

您可查看整个示例,方法是滚动到本主题末尾,下载示例到您的本地计算机或生成此示例。 有关更多信息和说明,请参阅设置示例

在您查看示例时,您会注意到突出显示重点的星号。 我们建议您阅读本节以更好地了解这些点,尤其是您之前未曾使用过 CancellationToken 的情况下。

若要实现取消按钮,您的代码必须包括以下元素。

  • 一个 CancellationTokenSource 变量,cts,这在所有可对其进行访问的方法范围内。

    Public NotInheritable Class MainPage
        Inherits Page
    
        ' ***Declare a System.Threading.CancellationTokenSource. 
        Dim cts As CancellationTokenSource
    
    public sealed partial class MainPage : Page
    {
        // ***Declare a System.Threading.CancellationTokenSource.
        CancellationTokenSource cts;
    
  • “取消”按钮的事件处理程序。 当用户请求取消时,事件处理程序使用 CancellationTokenSource.Cancel 方法通知 cts。

    ' ***Add an event handler for the Cancel button. 
    Private Sub cancelButton_Click(sender As Object, e As RoutedEventArgs)
        If cts IsNot Nothing Then
            cts.Cancel()
            ResultsTextBox.Text &= vbCrLf & "Downloads canceled by the Cancel button." 
        End If 
    End Sub
    
    // ***Add an event handler for the Cancel button. 
    private void CancelButton_Click(object sender, RoutedEventArgs e)
    {
        if (cts != null)
        {
            cts.Cancel();
            ResultsTextBox.Text += "\r\nDownloads canceled by the Cancel button.";
        }
    }
    
  • “开始”按钮和 StartButton_Click 的事件处理程序,包含以下操作。

    • 事件处理程序将 CancellationTokenSource,cts 实例化。

      cts = New CancellationTokenSource()
      
      // ***Instantiate the CancellationTokenSource.
      cts = new CancellationTokenSource();
      
    • 在调用 DownloadBlogsAsync 的过程中(下载博客订阅源),代码将 cts 的 CancellationTokenSource.Token 属性当做参数进行发送。 如果请求取消,则 Token 属性会传播消息。

      Await DownloadBlogsAsync(cts.Token)
      
      await DownloadBlogsAsync(cts.Token);
      
    • DownloadBlogsAsync 调用包含于 try-catch 语句中,该语句包括在选择“取消”按钮时产生结果的 OperationCanceledException 的 catch 块。 异步方法的调用方定义要执行的操作。 此示例只显示消息。

      以下代码显示了完整的 try-catch 语句。

      Try 
          ' ***Send a token to carry the message if cancellation is requested.
          Await DownloadBlogsAsync(cts.Token)
      
          ' ***Check for cancellations. 
      Catch op As OperationCanceledException
          ' In practice, this catch block often is empty. It is used to absorb 
          ' the exception,
          ResultsTextBox.Text &= vbCrLf & "Cancellation exception bubbles up to the caller." 
      
          ' Check for other exceptions. 
      Catch ex As Exception
          ResultsTextBox.Text =
              "Page could not be loaded." & vbCrLf & "Exception: " & ex.ToString()
      End Try
      
      try
      {
          // ***Send a token to carry the message if cancellation is requested.
          await DownloadBlogsAsync(cts.Token);
      }
      // ***Check for cancellations. 
      catch (OperationCanceledException)
      {
          // In practice, this catch block often is empty. It is used to absorb 
          // the exception,
          ResultsTextBox.Text += "\r\nCancellation exception bubbles up to the caller.";
      }
      // Check for other exceptions. 
      catch (Exception ex)
      {
          ResultsTextBox.Text =
              "Page could not be loaded.\r\n" + "Exception: " + ex.ToString();
      }
      
  • 如之前在此主题中所讨论的,DownloadBlogsAsync 方法调用 Windows 运行时 方法和 RetrieveFeedAsync,并将 .NET Framework 扩展方法和 AsTask 运用于返回的 IAsyncOperation 实例。 AsTask 将实例表示为 Task,因此您可以将取消标记发送给异步操作。 当您选择“取消”按钮时,则该标记将传播消息。

    注意,通过使用 AsTask,代码可以同时传递同一 CancellationToken 实例至 Windows 运行时 方法 (RetrieveFeedAsync) 和 .NET Framework 方法 (DownloadBlogsAsync)。

    以下各行显示这部分的代码。

    Dim feed As SyndicationFeed = Await client.RetrieveFeedAsync(uri).AsTask(ct)
    
    SyndicationFeed feed = await client.RetrieveFeedAsync(uri).AsTask(ct);
    
  • 如果不取消应用程序,它将生成以下输出。

    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
        Windows Restart and Recovery Recipe, 3/21/2011 2:13:24 PM -07:00
    
    Extreme Windows Blog
        Samsung Series 9 27” PLS Display: Amazing Picture, 8/20/2012 2:41:48 PM -07:00
        NVIDIA GeForce GTX 660 Ti Graphics Card: Affordable Graphics Powerhouse, 8/16/2012 10:56:19 AM -07:00
        HP Z820 Workstation: Rising To the Challenge, 8/14/2012 1:57:01 PM -07:00
    
    Blogging Windows
        Windows Upgrade Offer Registration Now Available, 8/20/2012 1:01:00 PM -07:00
        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
    
    Windows for your Business
        What Windows 8 RTM Means for Businesses, 8/1/2012 9:01:00 AM -07:00
        Higher-Ed Learning with Windows 8, 7/26/2012 12:03:00 AM -07:00
        Second Public Beta of App-V 5.0 Now Available with Office Integration, 7/24/2012 10:07:26 AM -07:00
    
    Windows Experience Blog
        Tech Tuesday Live Twitter Chat with Microsoft Hardware, 8/20/2012 2:20:57 AM -07:00
        New Colors and New Artist Series Mice from Microsoft Hardware, 8/15/2012 12:06:35 AM -07:00
        Tech Tuesday Live Twitter Chat with HP on Keeping Kids Safe as They Head Back to School #winchat, 8/13/2012 12:24:18 PM -07:00
    
    Windows Security Blog
        Dealing with Fake Tech Support & Phone Scams, 6/16/2011 1:53:00 PM -07:00
        Combating social engineering tactics, like cookiejacking, to stay safer online, 5/28/2011 12:02:26 PM -07:00
        Windows 7 is now Common Criteria Certified!, 4/27/2011 9:35:01 AM -07:00
    
    Windows Home Server Blog
        Connecting Windows 8 Consumer Preview with Windows Home Server, 3/25/2012 9:06:00 AM -07:00
        Viridian PC Systems announces two new server models are available to order, 10/3/2011 12:36:00 PM -07:00
        PC Specialist to release Windows Home Server 2011, 9/27/2011 10:27:37 AM -07:00
    
    Springboard Series Blog
        Windows 8 Is Ready For Your Enterprise, 8/16/2012 9:59:00 AM -07:00
        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
    

    如果在应用程序完成下载内容之前选择“取消”按钮,则结果与以下输出相似。

    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
        Windows Restart and Recovery Recipe, 3/21/2011 2:13:24 PM -07:00
    
    Extreme Windows Blog
        Samsung Series 9 27” PLS Display: Amazing Picture, 8/20/2012 2:41:48 PM -07:00
        NVIDIA GeForce GTX 660 Ti Graphics Card: Affordable Graphics Powerhouse, 8/16/2012 10:56:19 AM -07:00
        HP Z820 Workstation: Rising To the Challenge, 8/14/2012 1:57:01 PM -07:00
    
    Blogging Windows
        Windows Upgrade Offer Registration Now Available, 8/20/2012 1:01:00 PM -07:00
        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
    
    Windows for your Business
        What Windows 8 RTM Means for Businesses, 8/1/2012 9:01:00 AM -07:00
        Higher-Ed Learning with Windows 8, 7/26/2012 12:03:00 AM -07:00
        Second Public Beta of App-V 5.0 Now Available with Office Integration, 7/24/2012 10:07:26 AM -07:00
    
    Downloads canceled by the Cancel button.
    Cancellation exception bubbles up to the caller.
    

设置示例

您可下载应用程序,自己生成应用程序,或者在不实现的情况下在本主题的末尾查看代码。 您的计算机上必须安装 Visual Studio 和 Windows 8 才能运行此应用程序。

下载完成的应用程序

  1. 异步示例:从 .NET 桥接到 Windows 运行时(AsTask &取消)下载压缩文件。

  2. 解压下载的文件,然后启动 Visual Studio。

  3. 在菜单栏上,依次选择**“文件”“打开”“项目/解决方案”**。

  4. 导航到保存解压缩示例代码的文件夹,然后打开解决方案 (.sln) 文件。

  5. 选择 F5 键生成并运行项目。

    运行代码若干次以验证是否可以在不同点撤消。

生成完成的应用程序

  1. 启动 Visual Studio。

  2. 在菜单栏上,选择**“文件”“新建**、“项目”

    将打开**“新建项目”**对话框。

  3. 在“已安装应用程序”的“模板”类别中,选择“Visual Basic”或“Visual C#”,然后选择“Windows 应用商店”。

  4. 从项目模板列表中,选择“空白应用程序 (XAML)”。

  5. 将项目命名为 BlogFeedWithCancellation,然后选择“确定”按钮。

    新项目出现在**“解决方案资源管理器”**中。

  6. 在“解决方案资源管理器”中,打开 MainPage.xaml 的快捷菜单,然后选择“打开”。

  7. 在 MainPage.xaml 的“XAML”窗口中,使用下面的代码替换代码。

    <Page
        x:Class="BlogFeedWithCancellation.MainPage"
        xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:BlogFeedWithCancellation"
        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="Left" Margin="325,77,0,0" VerticalAlignment="Top" Click="StartButton_Click" Height="145" Background="#FFA89B9B" FontSize="36" Width="355"  />
            <Button x:Name="CancelButton" Content="Cancel" HorizontalAlignment="Left" Margin="684,77,0,0" VerticalAlignment="Top" Height="145" Background="#FFA89B9B" Click="CancelButton_Click" FontSize="36" Width="355"  />
            <TextBox x:Name="ResultsTextBox" HorizontalAlignment="Left" Margin="325,222,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="546" FontSize="10" ScrollViewer.VerticalScrollBarVisibility="Visible" Width="711" />
        </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 Tasks. 
    Imports System.Threading.Tasks
    ' Add an Imports statement for CancellationToken. 
    Imports System.Threading
    
    
    Public NotInheritable Class MainPage
        Inherits Page
    
        ' ***Declare a System.Threading.CancellationTokenSource. 
        Dim cts As CancellationTokenSource
    
        Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)
            ResultsTextBox.Text = "" 
            ' Prevent unexpected reentrance.
            StartButton.IsEnabled = False 
    
            ' ***Instantiate the CancellationTokenSource.
            cts = New CancellationTokenSource()
    
            Try 
                ' ***Send a token to carry the message if cancellation is requested.
                Await DownloadBlogsAsync(cts.Token)
    
                ' ***Check for cancellations. 
            Catch op As OperationCanceledException
                ' In practice, this catch block often is empty. It is used to absorb 
                ' the exception,
                ResultsTextBox.Text &= vbCrLf & "Cancellation exception bubbles up to the caller." 
    
                ' Check for other exceptions. 
            Catch ex As Exception
                ResultsTextBox.Text =
                    "Page could not be loaded." & vbCrLf & "Exception: " & ex.ToString()
            End Try 
    
            ' ***Set the CancellationTokenSource to null when the work is complete.
            cts = Nothing 
    
            ' In case you want to try again.
            StartButton.IsEnabled = True 
        End Sub 
    
    
        ' Provide a parameter for the CancellationToken.
        Async Function DownloadBlogsAsync(ct As CancellationToken) As Task
            Dim client As Windows.Web.Syndication.SyndicationClient = New SyndicationClient()
    
            Dim uriList = CreateUriList()
    
            ' Force the SyndicationClient to download the information.
            client.BypassCacheOnRetrieve = True 
    
    
            ' The following code avoids the use of implicit typing (var) so that you  
            ' can identify the types clearly. 
    
            For Each uri In uriList
                ' ***These three lines are combined in the single statement that follows them. 
                'Dim feedOp As IAsyncOperationWithProgress(Of SyndicationFeed, RetrievalProgress) = 
                '    client.RetrieveFeedAsync(uri) 
                'Dim feedTask As Task(Of SyndicationFeed) = feedOp.AsTask(ct) 
                'Dim feed As SyndicationFeed = Await feedTask 
    
                ' ***You can combine the previous three steps in one expression. 
                Dim feed As SyndicationFeed = Await client.RetrieveFeedAsync(uri).AsTask(ct)
    
                DisplayResults(feed, ct)
            Next 
        End Function 
    
    
        ' ***Add an event handler for the Cancel button. 
        Private Sub cancelButton_Click(sender As Object, e As RoutedEventArgs)
            If cts IsNot Nothing Then
                cts.Cancel()
                ResultsTextBox.Text &= vbCrLf & "Downloads canceled by the Cancel button." 
            End If 
        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/business/atom.aspx"),
                        New Uri("https://windowsteamblog.com/windows/b/windowsexperience/atom.aspx"),
                        New Uri("https://windowsteamblog.com/windows/b/windowssecurity/atom.aspx"),
                        New Uri("https://windowsteamblog.com/windows/b/windowshomeserver/atom.aspx"),
                        New Uri("https://windowsteamblog.com/windows/b/springboard/atom.aspx")
                    }
            Return uriList
        End Function 
    
    
        ' You can pass the CancellationToken to this method if you think you might use a 
        ' cancellable API here in the future. 
        Sub DisplayResults(sf As SyndicationFeed, ct As CancellationToken)
            ' Title of the blog.
            ResultsTextBox.Text &= sf.Title.Text & vbCrLf
    
            ' Titles and dates for the first three blog posts. 
            For i As Integer = 0 To If(sf.Items.Count >= 3, 2, sf.Items.Count)
                ResultsTextBox.Text &= vbTab & sf.Items.ElementAt(i).Title.Text & ", " &
                        sf.Items.ElementAt(i).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 Tasks. 
    using System.Threading.Tasks;
    // Add a using directive for CancellationToken. 
    using System.Threading;
    
    
    namespace BlogFeedWithCancellation
    {
        public sealed partial class MainPage : Page
        {
            // ***Declare a System.Threading.CancellationTokenSource.
            CancellationTokenSource cts;
    
            public MainPage()
            {
                this.InitializeComponent();
            }
    
    
            private async void StartButton_Click(object sender, RoutedEventArgs e)
            {
                ResultsTextBox.Text = "";
                // Prevent unexpected reentrance.
                StartButton.IsEnabled = false;
    
                // ***Instantiate the CancellationTokenSource.
                cts = new CancellationTokenSource();
    
                try
                {
                    // ***Send a token to carry the message if cancellation is requested.
                    await DownloadBlogsAsync(cts.Token);
                }
                // ***Check for cancellations. 
                catch (OperationCanceledException)
                {
                    // In practice, this catch block often is empty. It is used to absorb 
                    // the exception,
                    ResultsTextBox.Text += "\r\nCancellation exception bubbles up to the caller.";
                }
                // Check for other exceptions. 
                catch (Exception ex)
                {
                    ResultsTextBox.Text =
                        "Page could not be loaded.\r\n" + "Exception: " + ex.ToString();
                }
    
                // ***Set the CancellationTokenSource to null when the work is complete.
                cts = null;
    
                // In case you want to try again.
                StartButton.IsEnabled = true;
            }
    
    
            // ***Provide a parameter for the CancellationToken.
            async Task DownloadBlogsAsync(CancellationToken ct)
            {
                Windows.Web.Syndication.SyndicationClient client = new SyndicationClient();
    
                var uriList = CreateUriList();
    
                // Force the SyndicationClient to download the information.
                client.BypassCacheOnRetrieve = true;
    
                // The following code avoids the use of implicit typing (var) so that you  
                // can identify the types clearly. 
    
                foreach (var uri in uriList)
                {
                    // ***These three lines are combined in the single statement that follows them. 
                    //IAsyncOperationWithProgress<SyndicationFeed, RetrievalProgress> feedOp =  
                    //    client.RetrieveFeedAsync(uri); 
                    //Task<SyndicationFeed> feedTask = feedOp.AsTask(ct); 
                    //SyndicationFeed feed = await feedTask; 
    
                    // ***You can combine the previous three steps in one expression.
                    SyndicationFeed feed = await client.RetrieveFeedAsync(uri).AsTask(ct);
    
                    DisplayResults(feed);
                }
            }
    
    
    
            // ***Add an event handler for the Cancel button. 
            private void CancelButton_Click(object sender, RoutedEventArgs e)
            {
                if (cts != null)
                {
                    cts.Cancel();
                    ResultsTextBox.Text += "\r\nDownloads canceled by the Cancel button.";
                }
            }
    
    
            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/business/atom.aspx"),
                    new Uri("https://windowsteamblog.com/windows/b/windowsexperience/atom.aspx"),
                    new Uri("https://windowsteamblog.com/windows/b/windowssecurity/atom.aspx"),
                    new Uri("https://windowsteamblog.com/windows/b/windowshomeserver/atom.aspx"),
                    new Uri("https://windowsteamblog.com/windows/b/springboard/atom.aspx")
                };
                return uriList;
            }
    
    
            // You can pass the CancellationToken to this method if you think you might use a 
            // cancellable API here in the future. 
            void DisplayResults(SyndicationFeed sf)
            {
                // Title of the blog.
                ResultsTextBox.Text += sf.Title.Text + "\r\n";
    
                // Titles and dates for the first three blog posts. 
                for (int i = 0; i < (sf.Items.Count < 3 ? sf.Items.Count : 3); i++)    // Is Math.Min better?
                {
                    ResultsTextBox.Text += "\t" + sf.Items.ElementAt(i).Title.Text + ", " +
                        sf.Items.ElementAt(i).PublishedDate.ToString() + "\r\n";
                }
    
                ResultsTextBox.Text += "\r\n";
            }
        }
    }
    
  10. 选择 F5 键运行程序,然后选择“开始”按钮。

请参见

概念

WhenAny:.NET Framework 和 Windows 运行时之间的桥接(C# 和 Visual Basic)

取消一个异步任务或一组任务(C# 和 Visual Basic)

在一段时间后取消异步任务(C# 和 Visual Basic)

在完成一个异步任务后取消剩余任务(C# 和 Visual Basic)

取消标记