다음을 통해 공유


비동기 취소: .NET Framework와 Windows 런타임 간 브리징(C# 및 Visual Basic)

.NET Framework 및 Windows 런타임 기능을 결합하여 리소스를 극대화할 수 있습니다. 이 항목의 예에서는 웹에서 블로그 피드를 다운로드하기 위해 .NET Framework CancellationToken의 인스턴스를 사용하여 Windows 런타임 메서드를 사용하는 응용 프로그램에 취소 단추를 추가하는 방법을 보여 줍니다.

참고

예제를 실행하려면 컴퓨터에 Windows 8이 설치되어 있어야 합니다.또한 Visual Studio에서 예제를 실행하려면 Visual Studio 2012, Visual Studio 2013, Windows 8용 Visual Studio Express 2012 또는 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);
    }
}

루프에서 주석 처리된 부분은 전환 단계를 자세히 보여줍니다.

  • SyndicationClient.RetrieveFeedAsync에 대한 호출은 지정된 URI에서 블로그 피드를 다운로드하는 비동기 작업을 시작합니다. 비동기 작업은 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에 대한 호출은 취소 단추를 선택할 때 발생하는 OperationCanceledException에 대한 catch 블록을 포함하는 try-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)

취소 토큰