zoukankan      html  css  js  c++  java
  • .NET 4中的多线程编程之一:使用Task(转载)

    原文地址:http://www.cnblogs.com/yinzixin/archive/2011/11/04/2235233.html

    .NET 4为了简化多线程编程,提供了System.Threading.Tasks命名空间中的类来帮助开发者进行多线程编程,其中,Task类用来表示一个线程。最简单的Task类接受一个Action委托作为要执行的方法,调用Start方法开始在另一个线程中运行。例如:

    using System;
    using System.Threading.Tasks;
    namespace TaskParallel
    {
        class Program
        {
            static void Main(string[] args)
            {
                Task task=new Task(()=> Console.WriteLine("Write from another thread"));
                task.Start(); 
                Console.WriteLine("Main Thread");
                Console.ReadLine();         
            }
        }
    }
    
    两句输出的顺序是不一定的,但是很有可能是:

    Main Thread 
    Write from another thread 

    也可以使用Task.Factory.StartNew方法,这个方法会构造一个Task并且立刻开始运行,相当于将Task的构造函数和Start方法连在一起执行。

    Task类还有一个构造函数可以接受Action<object>委托,用来向Action委托传递参数:

    static void Main(string[] args)
    {
        for (int i = 0; i < 5; i++)
        {
            Task t = new Task(obj => Console.WriteLine("Thread No " + obj), i);
            t.Start();
        }
        Console.ReadLine();   
    }

    输出的结果类似于:

    Thread No 2 
    Thread No 1 
    Thread No 3 
    Thread No 0 
    Thread No 4


    可以使用Task<T>类来获得返回值,T是返回值的类型,例如:

    static void Main(string[] args)
    {
         Task<int> t = new Task<int>(() =>
         {
             int s=0;
             for (int i = 0; i < 10000; i++)
                 s += i;
             return s;
          });
          t.Start();
          Console.WriteLine("I'm computing");
          Console.WriteLine(t.Result);
          Console.ReadLine();
    }
    结果为:

    I'm computing 
    49995000

    在访问t.Result的时候,.net 会保证此时Task的代码已经执行完毕,Result已经获得,否则该线程会阻塞,直到Result计算完毕。

    Task库提供了一种主动终止线程的方法,先创建一个CancellationTokenSource,将其Token属性通过Task构造函数传进去,在Task内部轮询token的IsCancellationReqeusted属性,如果检测到为true,则主动终止线程。在父线程内调用tokenSource的Cancel方法,可以终止线程。注意,这是线程主动终止自己的方法,必须在Task内的代码自己终止,.NET不会强行终止task线程,即使父线程调用了tokenSource的Cancel方法。

    例如下面的代码,如果在else语句块内没有break语句,子线程是不会终止的。

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace TaskParallel
    {
        class Program
        {
            static void Main(string[] args)
            {
                CancellationTokenSource tks = new CancellationTokenSource();
                CancellationToken token = tks.Token;
                long i = 0;
                Task task = new Task(() =>
                {               
                    while (true)
                    {
                        if (!token.IsCancellationRequested)
                            i++;
                        else
                        {
                            Console.WriteLine("Task is canceled, it looped "+i+" times");
                            break;
                        }
                    }
                },token);
                task.Start();
                Console.WriteLine("Press Enter to Cancel task");
                Console.ReadLine();
                tks.Cancel();
                Console.ReadLine();
            }
        }
    }

    还可以通过CancellationToken的Register方法给token注册一个委托,当调用tokenSource的Cancel方法的时候,这个委托会被执行。这个委托是在调用Cancel方法的线程中执行的。

    token.Register(() => { Console.WriteLine("Delegate Invoked"); });
     
    要挂起当前线程,等待一个线程执行完成,可以使用执行线程的Wait()方法,Wait方法有一些重载方法,可以指定等待的时间等,例如:
    static void Main(string[] args)
    {
        Task t1 = new Task(() =>
        {
            Console.WriteLine("Task 1 Starts...");
            Thread.Sleep(3000);
            Console.WriteLine("Task1 Ends");
        });
        t1.Start();
        Task t2 = new Task(() =>
        {
            Console.WriteLine("Task2 Starts...");
            t1.Wait();
            Console.WriteLine("Task2 Ends");
        });
        Task t3 = new Task(() =>
            {
                Console.WriteLine("Task 3 is waiting Task1 for 1 second...");
                bool finished=t1.Wait(1000);
                if (finished)
                    Console.WriteLine("T1 is finished");
                else
                    Console.WriteLine("T1 is not finished");                   
            });
        t2.Start();
        t3.Start();
        Console.ReadLine();
    }

    执行结果为:

    Task 1 Starts... 
    Task2 Starts... 
    Task 3 is waiting Task1 for 1 second... 
    T1 is not finished 
    Task1 Ends 
    Task2 Ends 

    要等到多个线程都执行完可以使用Task.WaitAll方法,

    Task t4 = new Task(() =>
    {
        Console.WriteLine("Waiting for all");
        Task.WaitAll(t1, t2, t3);
        Console.WriteLine("Task4 Ends");
    });
    t4.Start();

    还有一个Task.WaitAny,可以等待一组线程中的任何一个方法执行完毕,用法类似。

    下面介绍Task中的异常处理,通常情况下,线程委托中的异常会导致线程终止,但异常并不会被抛出,例如:

    Task t = new Task(() =>
    {
        throw new Exception();
        Console.WriteLine("Thread Ends");
    });
    t.Start();
    Console.WriteLine("Main Ends");

    输出的是:

    Main Ends

    当调用Wait,WaitAll,WaitAny,Task.Result的时候,会抛出AggerateException ,在AggerateExcepiton中可以处理所有线程抛出的异常:

    static void Main(string[] args)
           {
               Task t1 = new Task(() =>
               {
                   throw new Exception();
                   Console.WriteLine("T1 Ends");
               });
    
               Task t2 = new Task(() =>
               {
                   throw new ArgumentException();
                   Console.WriteLine("T2 Ends");
               });
               t1.Start();
               t2.Start(); 
               try
               {
                   Task.WaitAll(t1, t2);
               }
               catch (AggregateException ex)
               {
                   foreach (var inner in ex.InnerExceptions)
                   {
                       Console.WriteLine(inner.GetType() + " " + inner.Source);
                   }
               }
               Console.WriteLine("Main Ends");
           }
       }

    有时候需要区分对待某些异常,一些异常直接处理掉,一些异常需要再次抛出,AggregateException提供一个Handle方法,接收一个Func<Exception,bool>委托作为参数,如果不需要再次抛出则返回true,否则返回false。

    try
    {
           Task.WaitAll(t1, t2);
    }
    catch (AggregateException ex)
    {
            foreach (var inner in ex.InnerExceptions)
            {
                 Console.WriteLine(inner.GetType() + " " + inner.Source);
            }
            ex.Handle((e) =>
            {
                  if (e is ArgumentException)
                  {
                       Console.WriteLine("Argument Exception is captured");
                       return true;
                  }
                  else
                       return false;
             });
    }
    这样就会有未处理的异常被抛出,抛出的仍然是AggregateException.
  • 相关阅读:
    CodeForces 347B Fixed Points (水题)
    CodeForces 347A Difference Row (水题)
    CodeForces 346A Alice and Bob (数学最大公约数)
    CodeForces 474C Captain Marmot (数学,旋转,暴力)
    CodeForces 474B Worms (水题,二分)
    CodeForces 474A Keyboard (水题)
    压力测试学习(一)
    算法学习(一)五个常用算法概念了解
    C#语言规范
    异常System.Threading.Thread.AbortInternal
  • 原文地址:https://www.cnblogs.com/huangzelin/p/6763119.html
Copyright © 2011-2022 走看看