多个任务.当WPF应用程序中出现第一个异常时,所有任务都终止

本文关键字:任务 异常 终止 第一个 WPF 应用程序 | 更新日期: 2024-07-23 09:41:48

以下示例代码片段通过使用async/await编码技术读取/处理Web Url示例列表List<string> urls的内容来实现多任务处理功能(类似于MSDN演示示例:https://msdn.microsoft.com/en-us/library/jj155756.aspx)。出于测试目的,Url List<string> urls包含两个错误项。

清单1。WPF异步/等待多任务实现中的错误处理

    namespace ProcessTasksAsTheyFinish
    {
        public partial class MainWindow : Window
        {
            CancellationTokenSource cts;
            // sample Url list containing erroneous items
            private List<string> SetUpURLList()
            {
                List<string> urls = new List<string> 
                { 
                    "http://msdn.microsoft.com",
                    "http://msdn.microsoft.com/library/windows/apps/br211380.aspx",
                    "error1",
                    "http://msdn.microsoft.com/en-us/library/aa578028.aspx",
                    "error2",
                    "http://msdn.microsoft.com/en-us/library/ms404677.aspx",
                    "http://msdn.microsoft.com/en-us/library/ff730837.aspx"
                };
                return urls;
            }
            public MainWindow() { InitializeComponent(); }
            private async void startButton_Click(object sender, RoutedEventArgs e) {
                resultsTextBox.Clear();
                cts = new CancellationTokenSource();
                try { await AccessTheWebAsync(cts.Token);}
                finally { cts = null; }
            }
            private async Task AccessTheWebAsync(CancellationToken ct) {
                try {
                    HttpClient client = new HttpClient();
                    // sample list of web addresses
                    List<string> urlList = SetUpURLList();
                    // query to create a collection of Tasks
                    IEnumerable<Task<int>> downloadTasksQuery =
                        from url in urlList select ProcessURL(url, client, ct);
                    // run multiple Tasks in async mode
                    await Task.WhenAll(downloadTasksQuery.ToList());
                    resultsTextBox.Text += "'r'nDownloads complete.";
                }
                catch (OperationCanceledException){
                    resultsTextBox.Text += "'r'nDownloads canceled.";
                }
                catch (Exception ex){
                    resultsTextBox.Text += Environment.NewLine + ex.Message;
                }
            }
            private async Task<int> ProcessURL(string url, HttpClient client, CancellationToken ct)
            {
                try
                {
                    HttpResponseMessage response = await client.GetAsync(url, ct);
                    byte[] urlContents = await response.Content.ReadAsByteArrayAsync();
                    resultsTextBox.Text += String.Format("'r'nLength:  {0}", urlContents.Length);
                    return urlContents.Length;
                }
                catch 
                { 
                    //if (cts != null)  cts.Cancel(); 
                    throw; 
                }
            }
        }
    }
// sample output:
// Length:  196315
// Length:  468941
// Length:  158496
// Length:  200790
// Length:  48022
// An invalid request URI was provided. The request URI must either be an absolute URI or BaseAddress must be set.

输出包含为5个有效Url计算的长度信息和与异常ex.Message相对应的错误通知。

目标是以这样的方式修改功能,即第一个异常将完全终止await Task.WhenAll的多任务执行。换句话说,实现与等待的多个Tasks相关的那种"要么全有要么全无"的业务逻辑。

在本例中,它是通过使用CancellationToken(如清单1中的注释行所示)向Exception处理块添加以下语句来实现的:

if (cts != null)  cts.Cancel();

输出文本看起来如预期,对应于ex.Message:

提供了无效的请求URI。请求URI必须是必须设置绝对URI或BaseAddress。

已编辑:我正在寻找相同"要么全有要么全无"功能的简化实现(能够在出现第一个错误时终止Task.WhenAll过程中的整套Tasks),而不使用CancellationToken。根据@Stephen Cleary发布的富有洞察力的评论,没有任何过载的"快捷方式"选项,也没有比当前更好的解决方案(就简单性而言)。

多个任务.当WPF应用程序中出现第一个异常时,所有任务都终止

没有提供这种内置功能的重载。

通常,CancellationTokenSource对象的自动取消不在BCL中(超时便利方法除外,该方法基于计时器自动取消)。如果我大胆猜测的话,我会说BCL团队认为这类重载有太多不同的用例,所以它们对足够广泛的受众来说不够有用,无法内置。

相反,适当的解决方案是在任何一个任务出现故障时自己触发CTS,这就是您的代码已经在做的事情。