日期:2014-05-16  浏览次数:20953 次

C#多线程(二)

一、线程池

每次创建一个线程,都会花费几百微秒级别的时间来创建一个私有的局部栈,每个线程默认使用1M的内存。这个可以在使用Thread类的构造函数时设置:

new Thread(new ThreadStart(Go), 2);
new Thread(new ParameterizedThreadStart(Go("hello")), 3);
提供的两种构造函数方式都提供了对应的设置线程局部栈的大小。线程池通过共享和回收线程的方式来分配这些内存,这样可以使多线程运行在一个非常细粒度级别上而不影响性能。这对于充分利用多核处理器,使用分而治之的方式进行密集型计算的程序中很有用。同时线程池维护一个所有同时运行的工作线程总数的上限,如果有过多的活动线程就会加重操作系统的负担,使诸如CPU缓存失效等问题,当达到这个上限后,就要进行排队。这个线程队列使得任意并发的应用成为可能,如Web服务器就是这种原理。

有多种方式进入线程池:

  • 通过Task Parallel Library(.NET 4  TPL)
  • 通过调用ThreadPool.QueueUserWorkItem
  • 通过异步委托方式
  • 使用BackgroundWorker
下面的应用间接地使用了线程池:
  • WCF、Remoting、ASP.NET、ASMX webservice
  • System.Timers.Timer和System.Threading.Timer
  • .NET中以Async结束命名的方法
  • PLINQ
注意事项:
  • 不能对放入线程池中的线程命名,这会使得调试更加困难
  • 线程池中的线程总是后台线程
  • 阻塞线程池中的线程困难引发额外的延迟
可以使用下面的方式查询当前线程是否在线程池中:
CurrentTread.IsThreadPoolThread

二、进入线程池

1、使用TPL进入
使用TPL中的Task类就可以很简单的进入,使用Task.Factory.StartNew方法,传递一个目标函数的委托即可:
        static void Main(string[] args)
        {
            Task.Factory.StartNew(Go);

            Console.WriteLine("Is Thread Pool: " + Thread.CurrentThread.IsThreadPoolThread.ToString());
            Console.ReadKey();
        }
        static void Go()
        {
            Console.Write("Is Thread Pool: " + Thread.CurrentThread.IsThreadPoolThread.ToString());
        }

Task.Factory.StartNew返回一个Task对象可以用来监控这个任务。
同时可以使用泛型类Task<TResult>,如下:
        static void Main(string[] args)
        {
            Task<string> task = Task.Factory.StartNew<string>(Go);
            
            Console.WriteLine("Is Thread Pool: " + Thread.CurrentThread.IsThreadPoolThread.ToString());

            if (task.IsCompleted)
            {
                string result = task.Result;
                Console.WriteLine(task.IsCompleted.ToString() +  result);
            }
            Console.ReadKey();
        }
        static string Go()
        {
            return Thread.CurrentThread.IsThreadPoolThread.ToString();
        }

如果在工作线程中出现异常,当获取Task的Result属性时会重新引发AggregateException异常,如果没有查询Result或者没有调用Wait方法,所有未处理的异常都会终止进程执行。
2、不使用TPL
对于.NET 4.0以前的版本是无法使用TPL的,必须使用Thread