.NET 并行(多核)编程系列之五 Task执行和异常处理

简介: 原文:.NET 并行(多核)编程系列之五 Task执行和异常处理.NET 并行(多核)编程系列之五 Task执行和异常处理   前言:本篇主要讲述等待task执行完成。  本篇的议题如下:  1. 等待Task执行完成  2.
原文: .NET 并行(多核)编程系列之五 Task执行和异常处理

.NET 并行(多核)编程系列之五 Task执行和异常处理

  前言:本篇主要讲述等待task执行完成。
  本篇的议题如下:
  1. 等待Task执行完成
  2. Task中的异常处理

 

  系列文章链接:

  .NET 4 并行(多核)编程系列之一入门介绍

  .NET 4 并行(多核)编程系列之二 从Task开始 

  .NET 4 并行(多核)编程系列之三 从Task的取消 

  .NET 4 并行(多核)编程系列之四 Task的休眠 

  .NET 并行(多核)编程系列之五 Task执行和异常处理 

  .NET 并行(多核)编程系列之六 Task基础部分完结篇 

  .NET 并行(多核)编程系列之七 共享数据问题和解决概述

 

  首先注意一点:这里提到的"等待"和之前文章提到的"休眠"意思是不一样的:
  等待:在等待一个task的时候,这个task还是在运行之中的,"等待"相当于在监听运行的task的执行情况。
  休眠:让tasku不运行。

 

  在上篇文章中介绍了如果从Task中获取执行后的结果:在Task执行完成之后调用Task.Result获取。其实也可以用其他的方法等待Task执行完成而不获取结果,这是很有用的:如果你想等待一个task完成之后再去做其他的事情。而且我们还可以等待一个task执行完成,或者等待所有的task执行完成,或者等待很多task中的一个执行完成。因为Task是由内部的Scheduler管理的,调用wait方法,其实就是我们在监控task的执行,看看这个task是否执行完了,如果完成,那么wanit方法就返回true,反之。

 

  1. 等待Task执行完成

  1.1等待单独的一个Task执行完成
  我们可以用Wait()方法来一直等待一个Task执行完成。当task执行完成,或者被cancel,或者抛出异常,这个方法才会返回。可以使用Wait()方法的不同重载。举个例子:
 

 

img_405b18b4b6584ae338e0f6ecaf736533.gif 代码
  static   void  Main( string [] args)
    {
        
//  create the cancellation token source
        CancellationTokenSource tokenSource  =   new  CancellationTokenSource();
        
//  create the cancellation token
        CancellationToken token  =  tokenSource.Token;
        
//  create and start the first task, which we will let run fully
        Task task  =  createTask(token);
        task.Start();

        
//  wait for the task
        Console.WriteLine( " Waiting for task to complete. " );
        task.Wait();
        Console.WriteLine(
" Task Completed. " );

        
//  create and start another task
        task  =  createTask(token);
        task.Start();
        Console.WriteLine(
" Waiting 2 secs for task to complete. " );
        
bool  completed  =  task.Wait( 2000 );
        Console.WriteLine(
" Wait ended - task completed: {0} " , completed);

        
//  create and start another task
        task  =  createTask(token);
        task.Start();
        Console.WriteLine(
" Waiting 2 secs for task to complete. " );
        completed 
=  task.Wait( 2000 , token);
        Console.WriteLine(
" Wait ended - task completed: {0} task cancelled {1} " ,
        completed, task.IsCanceled);

        
//  wait for input before exiting
        Console.WriteLine( " Main method complete. Press enter to finish. " );
        Console.ReadLine();
    }

    
static  Task createTask(CancellationToken token)
    {
        
return   new  Task(()  =>
        {
            
for  ( int  i  =   0 ; i  <   5 ; i ++ )
            {
                
//  check for task cancellation
                token.ThrowIfCancellationRequested();
                
//  print out a message
                Console.WriteLine( " Task - Int value {0} " , i);
                
//  put the task to sleep for 1 second
                token.WaitHandle.WaitOne( 1000 );
            }
        }, token);
    }

 

  

  从上面的例子可以看出,wait方法子task执行完成之后会返回true。
  注意:当在执行的task内部抛出了异常之后,这个异常在调用wait方法时会被再次抛出。后面再"异常处理篇"会讲述。

 

  1.2.等待多个task
  我们也可以用WaitAll()方法来一直到等待多个task执行完成。只有当所有的task执行完成,或者被cancel,或者抛出异常,这个方法才会返回。WiatAll()方法和Wait()方法一样有一些重载。
  注意:如果在等在的多个task之中,有一个task抛出了异常,那么调用WaitAll()方法时就会抛出异常。


 

img_405b18b4b6584ae338e0f6ecaf736533.gif 代码
static   void  Main( string [] args)
    {
        
//  create the cancellation token source
        CancellationTokenSource tokenSource  =   new  CancellationTokenSource();
        
//  create the cancellation token
        CancellationToken token  =  tokenSource.Token;
        
//  create the tasks
        Task task1  =   new  Task(()  =>
        {
            
for  ( int  i  =   0 ; i  <   5 ; i ++ )
            {
                
//  check for task cancellation
                token.ThrowIfCancellationRequested();
                
//  print out a message
                Console.WriteLine( " Task 1 - Int value {0} " , i);
                
//  put the task to sleep for 1 second
                token.WaitHandle.WaitOne( 1000 );
            }
            Console.WriteLine(
" Task 1 complete " );
        }, token);
        Task task2 
=   new  Task(()  =>
        {
            Console.WriteLine(
" Task 2 complete " );
        }, token);

        
//  start the tasks
        task1.Start();
        task2.Start();
        
//  wait for the tasks
        Console.WriteLine( " Waiting for tasks to complete. " );
        Task.WaitAll(task1, task2);
        Console.WriteLine(
" Tasks Completed. " );
        
//  wait for input before exiting
        Console.WriteLine( " Main method complete. Press enter to finish. " );
        Console.ReadLine();
    }

 

   在上面的例子中,首先创建了两个task,注意我们创建的是可以被cancel的task,因为使用CancellationToken。而且在第一个task中还是用waitOne()休眠方法,其实目的很简单:使得这个task的运行时间长一点而已。之后我们就调用了WaitAll()方法,这个方法一直到两个task执行完成之后才会返回的。

 

  1.3.等待多个task中的一个task执行完成
  可以用WaitAny()方法来等待多个task中的一个task执行完成。通俗的讲就是:有很多的task在运行,调用了WaitAny()方法之后,只要那些运行的task其中有一个运行完成了,那么WaitAny()就返回了。
 


img_405b18b4b6584ae338e0f6ecaf736533.gif 代码
static   void  Main( string [] args)
    {

        
//  create the cancellation token source
        CancellationTokenSource tokenSource  =   new  CancellationTokenSource();
        
//  create the cancellation token
        CancellationToken token  =  tokenSource.Token;
        
//  create the tasks
        Task task1  =   new  Task(()  =>
        {
            
for  ( int  i  =   0 ; i  <   5 ; i ++ )
            {
                
//  check for task cancellation
                token.ThrowIfCancellationRequested();
                
//  print out a message
                Console.WriteLine( " Task 1 - Int value {0} " , i);
                
//  put the task to sleep for 1 second
                token.WaitHandle.WaitOne( 1000 );
            }
            Console.WriteLine(
" Task 1 complete " );
        }, token);
        Task task2 
=   new  Task(()  =>
        {
            Console.WriteLine(
" Task 2 complete " );
        }, token);
        
//  start the tasks
        task1.Start();
        task2.Start();
        
//  wait for the tasks
        Console.WriteLine( " Waiting for tasks to complete. " );
        
int  taskIndex  =  Task.WaitAny(task1, task2);
        Console.WriteLine(
" Task Completed. Index: {0} " , taskIndex);
        
//  wait for input before exiting
        Console.WriteLine( " Main method complete. Press enter to finish. " );
        Console.ReadLine();
    }

 

  

  2. Task中的异常处理

  在并行编程(TPL)中另外一个已经标准化了的操作就是"异常处理"。而且在并行编程中异常处理显得尤为重要,因为并行编程时与系统中的线程相关的,出了异常,你开发的程序就很有可能崩溃。

 

  下面就详细介绍TPL中异常处理操作。
   a.处理基本的异常。
   在操作task的时候,只要出现了异常,.NET Framework就会把这些异常记录下来。例如在执行Task.Wait(),Task.WaitAll(),Task.WaitAny(),Task.Result.不管那里出现了异常,最后抛出的就是一个System.AggregateException.

   System.AggregateException时用来包装一个或者多个异常的,这个类时很有用的,特别是在调用Task.WaitAll()方法时。因为在Task.WaitAll()是等待多个task执行完成,如果有任意task执行出了异常,那么这个异常就会被记录在System.AggregateException中,不同的task可能抛出的异常不同,但是这些异常都会被记录下来。


  下面就是给出一个例子:在例子中,创建了两个task,它们都抛出异常。然后主线程开始运行task,并且调用WaitAll()方法,然后就捕获抛出的System.AggregateException,显示详细信息。
 

 

img_405b18b4b6584ae338e0f6ecaf736533.gif 代码
static   void  Main( string [] args)
    {

        
//  create the tasks
        Task task1  =   new  Task(()  =>
        {
            ArgumentOutOfRangeException exception 
=   new  ArgumentOutOfRangeException();
            exception.Source 
=   " task1 " ;
            
throw  exception;
        });
        Task task2 
=   new  Task(()  =>
        {
            
throw   new  NullReferenceException();
        });
        Task task3 
=   new  Task(()  =>
        {
            Console.WriteLine(
" Hello from Task 3 " );
        });
        
//  start the tasks
        task1.Start(); task2.Start(); task3.Start();
        
//  wait for all of the tasks to complete
        
//  and wrap the method in a try...catch block
         try
        {
            Task.WaitAll(task1, task2, task3);
        }
        
catch  (AggregateException ex)
        {
            
//  enumerate the exceptions that have been aggregated
             foreach  (Exception inner  in  ex.InnerExceptions)
            {
                Console.WriteLine(
" Exception type {0} from {1} " ,
                inner.GetType(), inner.Source);
            }
        }
        
//  wait for input before exiting
        Console.WriteLine( " Main method complete. Press enter to finish. " );
        Console.ReadLine();
    }

 

 

 从上面的例子可以看出,为了获得被包装起来的异常,需要调用System.AggregateException的InnerExceptions属性,这个属性返回一个异常的集合,然后就可以遍历这个集合。

  而且从上面的例子可以看到:Exeception.Source属性被用来指明task1的异常时ArgumentOutRangeException.


   b.使用迭代的异常处理Handler
   一般情况下,我们需要区分哪些异常需要处理,而哪些异常需要继续往上传递。AggregateException类提供了一个Handle()方法,我们可以用这个方法来处理

AggregateException中的每一个异常。在这个Handle()方法中,返回true就表明,这个异常我们已经处理了,不用抛出,反之。

   在下面的例子中,抛出了一个OperationCancelException,在之前的task的取消一文中,已经提到过:当在task中抛出这个异常的时候,实际上就是这个task发送了取消的请求。下面的代码中,描述了如果在AggregateException.Handle()中处理不同的异常。

 

 

img_405b18b4b6584ae338e0f6ecaf736533.gif 代码
static   void  Main( string [] args)
    {
        
//  create the cancellation token source and the token
        CancellationTokenSource tokenSource  =   new  CancellationTokenSource();
        CancellationToken token 
=  tokenSource.Token;
        
//  create a task that waits on the cancellation token
        Task task1  =   new  Task(()  =>
        {
            
//  wait forever or until the token is cancelled
            token.WaitHandle.WaitOne( - 1 );
            
//  throw an exception to acknowledge the cancellation
             throw   new  OperationCanceledException(token);
        }, token);
        
//  create a task that throws an exception
        Task task2  =   new  Task(()  =>
        {
            
throw   new  NullReferenceException();
        });
        
//  start the tasks
        task1.Start(); task2.Start();
        
//  cancel the token
        tokenSource.Cancel();
        
//  wait on the tasks and catch any exceptions
         try
        {
            Task.WaitAll(task1, task2);
        }
        
catch  (AggregateException ex)
        {
            
//  iterate through the inner exceptions using
            
//  the handle method
            ex.Handle((inner)  =>
            {
                
if  (inner  is  OperationCanceledException)
                {

                    
//  ...handle task cancellation...
                     return   true ;
                }
                
else
                {
                    
//  this is an exception we don't know how
                    
//  to handle, so return false
                     return   false ;
                }
            });
        }
        
//  wait for input before exiting
        Console.WriteLine( " Main method complete. Press enter to finish. " );
        Console.ReadLine();
    }

 

 

   今天就写到这里。谢谢!

   版权为小洋和博客园所有,转载请标明出处给作者。

    http://www.cnblogs.com/yanyangtian

目录
相关文章
|
5月前
|
存储 JSON 开发工具
Visual Studio编程效率提升技巧集(提高.NET编程效率)
Visual Studio编程效率提升技巧集(提高.NET编程效率)
Visual Studio编程效率提升技巧集(提高.NET编程效率)
|
2月前
|
传感器 数据采集 物联网
探索.NET nanoFramework:为嵌入式设备编程的新途径
探索.NET nanoFramework:为嵌入式设备编程的新途
56 7
|
2月前
.NET 4.0下实现.NET4.5的Task类相似功能组件
【10月更文挑战第29天】在.NET 4.0 环境下,可以使用 `BackgroundWorker` 类来实现类似于 .NET 4.5 中 `Task` 类的功能。`BackgroundWorker` 允许在后台执行耗时操作,同时不会阻塞用户界面线程,并支持进度报告和取消操作。尽管它有一些局限性,如复杂的事件处理模型和不灵活的任务管理方式,但在某些情况下仍能有效替代 `Task` 类。
|
4月前
|
大数据 开发工具 开发者
从零到英雄:.NET核心技术带你踏上编程之旅,构建首个应用,开启你的数字世界探险!
【8月更文挑战第28天】本文带领读者从零开始,使用强大的.NET平台搭建首个控制台应用。无论你是新手还是希望扩展技能的开发者,都能通过本文逐步掌握.NET的核心技术。从环境搭建到创建项目,再到编写和运行代码,详细步骤助你轻松上手。通过计算两数之和的小项目,你不仅能快速入门,还能为未来开发更复杂的应用奠定基础。希望本文为你的.NET学习之旅开启新篇章!
35 1
|
4月前
|
存储 C#
揭秘C#.Net编程秘宝:结构体类型Struct,让你的数据结构秒变高效战斗机,编程界的新星就是你!
【8月更文挑战第4天】在C#编程中,结构体(`struct`)是一种整合多种数据类型的复合数据类型。与类不同,结构体是值类型,意味着数据被直接复制而非引用。这使其适合表示小型、固定的数据结构如点坐标。结构体默认私有成员且不可变,除非明确指定。通过`struct`关键字定义,可以包含字段、构造函数及方法。例如,定义一个表示二维点的结构体,并实现计算距离原点的方法。使用时如同普通类型,可通过实例化并调用其成员。设计时推荐保持结构体不可变以避免副作用,并注意装箱拆箱可能导致的性能影响。掌握结构体有助于构建高效的应用程序。
134 7
|
4月前
|
Java Spring 自然语言处理
Spring 框架里竟藏着神秘魔法?国际化与本地化的奇妙之旅等你来揭开谜底!
【8月更文挑战第31天】在软件开发中,国际化(I18N)与本地化(L10N)对于满足不同地区用户需求至关重要。Spring框架提供了强大支持,利用资源文件和`MessageSource`实现多语言文本管理。通过配置日期格式和货币符号,进一步完善本地化功能。合理应用这些特性,可显著提升应用的多地区适应性和用户体验。
45 0
|
4月前
|
传感器 数据采集 物联网
探索未来:.NET nanoFramework引领嵌入式设备编程革新之旅
【8月更文挑战第28天】.NET nanoFramework 是一款专为资源受限的嵌入式设备设计的轻量级、高性能框架,基于 .NET Core,采用 C# 进行开发,简化了传统底层硬件操作的复杂性,极大提升了开发效率。开发者可通过 Visual Studio 或 Visual Studio Code 快速搭建环境并创建项目,利用丰富的库和驱动程序轻松实现从基础 LED 控制到网络通信等多种功能,显著降低了嵌入式开发的门槛。
86 0
|
6月前
|
SQL 开发框架 安全
【.NET Core】深入理解任务并行库 (TPL)
【.NET Core】深入理解任务并行库 (TPL)
75 0
|
7月前
|
JSON 编解码 Go
Golang深入浅出之-HTTP客户端编程:使用net/http包发起请求
【4月更文挑战第25天】Go语言`net/http`包提供HTTP客户端和服务器功能,简化高性能网络应用开发。本文探讨如何发起HTTP请求,常见问题及解决策略。示例展示GET和POST请求的实现。注意响应体关闭、错误处理、内容类型设置、超时管理和并发控制。最佳实践包括重用`http.Client`,使用`context.Context`,处理JSON以及记录错误日志。通过实践这些技巧,提升HTTP编程技能。
85 1
|
7月前
|
Go 开发者
Golang深入浅出之-HTTP客户端编程:使用net/http包发起请求
【4月更文挑战第24天】Go语言的`net/http`包在HTTP客户端编程中扮演重要角色,但使用时需注意几个常见问题:1) 检查HTTP状态码以确保请求成功;2) 记得关闭响应体以防止资源泄漏;3) 设置超时限制,避免长时间等待;4) 根据需求处理重定向。理解这些细节能提升HTTP客户端编程的效率和质量。
85 1