zoukankan      html  css  js  c++  java
  • .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()方法的不同重载。举个例子:
     

    代码
     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()方法时就会抛出异常。


     

    代码
    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()就返回了。
     


    代码
    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,显示详细信息。
     

    代码
    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()中处理不同的异常。

    代码
    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

  • 相关阅读:
    【loj6179】Pyh的求和
    【bzoj4036】按位或
    【CF472G】Design Tutorial: Increase the Constraints
    【bzoj4811】由乃的OJ
    双马尾机器人(???)
    【codechef】Children Trips
    【bzoj3796】Mushroom追妹纸
    【bzoj4571】美味
    前夕
    【bzoj3589】动态树
  • 原文地址:https://www.cnblogs.com/yanyangtian/p/1749199.html
Copyright © 2011-2022 走看看