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

    .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.
     
    
    
  • 相关阅读:
    我的博客园的博客开通啦
    设置cookie
    JavaScript自动提示
    补码
    vim快捷键
    JavaScript获取URL参数
    Linux根目录下子目录的功能
    JavaScript分页栏链接转变算法
    可输可选可自动提示,还可增加一个!
    VS2008启动调试,出现“ 已经找到网站 正在等待回应”
  • 原文地址:https://www.cnblogs.com/yinzixin/p/2235233.html
Copyright © 2011-2022 走看看