1. 线程池(ThreadPool)
 
为什么要使用线程池? 主要原因是创建和销毁一个线程的代价是昂贵的,会消耗较多的系统资源;线程池原理? 每个CLR只有一个线程池,线程池线程不是在CLR初始化时自动创建的,而是向线程池派发(dispatch)异步操作时,如果线程池中没有线程,则会创建一个新新线程,不同的是,这个线程不会被销毁,执行完后进入空闲状态,等待响应新的异步请求。 当然,如果一个线程池线程不做任何事情,也是一种资源浪费。所以,当一个线程空闲特定一段时间后,会自己醒来终止自己以释放资源。 请注意线程池中的线程都是后台线程,在所有的前台线程运行结束后,所有的后台线程将停止工作。 创建一个线程时,会将当前线程的上下文传递给新建线程,而收集和复制上下文信息会耗费一定的时间和性能,在不需要传递上下文的场景中,可以通过System.Threading.ExecutionContext类型中的SuppressFlow()方法和RestoreFlow()方法分别阻止和恢复上下文的传递或流动。使用线程池时的注意项? 
  线程池不适合需要长时间运行的作业,或者处理需要与其它线程同步的作业;避免在线程池中执行I/O首先的操作,这种任务应该使用TPL模型;不要手动设置线程池的最小和最大线程数,CLR会自动执行线程池的扩张和收缩,手动干预会使性能更差(目前默认是1000个线程); 线程池的两种使用方式: 
  通过异步编程模型(Asynchronous Programming Model,简称APM)展示怎样在线程池中异步的执行委托? 下面的方式为异步编程模型(这是.net历史中第一个异步编程模式),这里使用委托的BeginInvoke()方法来来运行该委托,BeginInvoke接收一个回调函数,会在任务执行完后被调用;现在这种APM编程模型使用的越来越少了,更多的是使用任务并行库(Task Parallel Library, 简称TPL)。  
	
private delegate string RunOnThreadPool(out int threadId
);
	static void Main(string[] args
)
	{
	    
	    IAsyncResult r 
= poolDelegate
.BeginInvoke(out threadId
, Callback
, "委托异步调用");
	    r
.AsyncWaitHandle
.WaitOne();
	    
	    string result 
= poolDelegate
.EndInvoke(out threadId
, r
);
	}
 
通过ThreadPool.QueueUserWorkItem()向线程池中放入异步操作? 
	
static void Main(string[] args
)
	{
	    
	    ThreadPool
.QueueUserWorkItem(AsyncOperation
);
	
	    
	    ThreadPool
.QueueUserWorkItem(AsyncOperation
, "async state");
	
	    
	    ThreadPool
.QueueUserWorkItem(state 
=>
	    {
	        WriteLine($
"Operation state: {state}");
	        WriteLine($
"工作线程 id: {CurrentThread.ManagedThreadId}");
	    }, "lambda state");
	}
	
	private static void AsyncOperation(object state
)
	{
	    WriteLine($
"Operation state: {state ?? "(null)"}");
	    WriteLine($
"工作线程 id: {CurrentThread.ManagedThreadId}");
	}
 
使用普通创建线程方式和线程池方式有何区别? 分别运行下面两个方法,其中普通线程执行了2s多,但是创建了500个线程,线程池执行了9s多,但是只创建了很少的线程,为操作系统节省了线程和内存空间,但是花费的时间较多; 
	
static void UseThreads()
	{
	    for (int i 
= 0; i 
< 500; i
++)
	    {
	        var thread 
= new Thread(() =>
	        {
	            Sleep(TimeSpan
.FromSeconds(0.1));
	        });
	        thread
.Start();
	    }
	}
	
	static void UseThreadPool()
	{
		 for (int i 
= 0; i 
< 500; i
++)
		 {
		     ThreadPool
.QueueUserWorkItem(c 
=>
		     {
		         Sleep(TimeSpan
.FromSeconds(0.1));
		     });
		 }
	}
 
如何通过CancellationTokenSource取消线程: 
	
private CancellationTokenSource cts 
= new CancellationTokenSource();
	private void StartThread(object sender
, RoutedEventArgs e
)
	{
	    ThreadPool
.QueueUserWorkItem(o 
=> Count(cts
.Token
, 100));
	}
	
	private void Count(CancellationToken token
, Int32 countTo
)
	{
	    for (Int32 count 
= 0; count 
< countTo
; count
++)
	    {
	        if (token
.IsCancellationRequested
)
	        {
	            this.Dispatcher
.Invoke(() =>
	            {
	                this.AutoAddTextBox
.Text 
+= '\n' + "Count is canceled" + '\n';
	            });
	            break;
	        }
	        this.Dispatcher
.Invoke(() =>
	        {
	            this.AutoAddTextBox
.Text 
+= count
.ToString() + " ";
	        });
	        Thread
.Sleep(100);
	    }
	    this.Dispatcher
.Invoke(() =>
	    {
	        this.AutoAddTextBox
.Text 
+= "Count is Done!" + '\n';
	    });
	}
	
	private void CancelThread(object sender
, RoutedEventArgs e
)
	{
		
	    cts
.Cancel();
	}
 
 当运行到第32次时取消线程。
 
BackgroundWorker组件介绍 BackgroundWorker是基于事件的异步编程模式( Event-based Asynchronous Pattern,简称EAP),是.net历史上第二种用来构造异步程序的方式,现在推荐使用TPL;该组件被应用于WPF中,通过它实现的代码可以直接与UI控制器交互; 
2、任务(Task)
 
什么是任务并行库(TPL)? 为了实现线程的同步、异步、异常传递等问题,需要编写较多的代码,来达到正确性和健壮性,而且最大的问题是没有内建的机制让你知道操作在什么时候完成,也没有机制在操作完成时获取返回值。为此,.NET 4.0引入了一个关于异步操作的API——任务并行库(TPL)。TPL的内部使用了线程池,而且效率更高。在把线程归还到线程池之前,它会在同一个线程中顺序执行多个任务,减少任务上下文切换带来的时间浪费问题。其中,任务(Task)是对象,封装了要异步执行的操作。 TPL被认为是线程池之上的又一抽象层,其对程序员隐藏了与线程池交互的底层代码,并提供了更方便的细粒度API,TPL的核心概念是任务Task。Task创建的是线程池任务,Thread默认创建的是前台线程;线程池一般只运行执行时间较短的异步操作;新建Task的方法有3种,示例如下: 其中Task2中使用的是Run()静态方法,Task4中设置了LongRunning,表明需要长时间运行,因此不是线程池线程;注意,Task.Run方法只是Task.Factory.StartNew的一个快捷方式,但是后者有附加的选项; 
   
public Main()
   {
       var task1 
= new Task(() => TaskMethod("Task 1"));
       task1
.Start();
       Task
.Run(() => TaskMethod("Task 2"));
       Task
.Factory
.StartNew(() => TaskMethod("Task 3"));
       Task
.Factory
.StartNew(() => TaskMethod("Task 4"), TaskCreationOptions
.LongRunning
);
   }
   
   private void TaskMethod(string name
)
   {
       string str 
= string.Format("{0} 线程id:{1},线程池中线程:{2}",
           name
, Thread
.CurrentThread
.ManagedThreadId
, Thread
.CurrentThread
.IsThreadPoolThread
);
       this.Dispatcher
.BeginInvoke(new Action(delegate
       {
           this.textbox
.Text 
+= str 
+ "\n";
       }));
   }
 
输出结果:
 
	Task 1 线程id:
10,线程池中线程:
True
	Task 2 线程id:
12,线程池中线程:
True
	Task 3 线程id:
13,线程池中线程:
True
	Task 4 线程id:
12,线程池中线程:False
 
Task的基本操作: 
    
    
TaskMethod("主线程任务");
    
    Task
<int> task 
= CreateTask("Task 1");
    task
.Start();
    task
.Wait();
    int result 
= task
.Result
;
    
    WriteLine($
"运算结果: {result}");
    
    task 
= CreateTask("Task 2");
    task
.RunSynchronously();
    result 
= task
.Result
;
    WriteLine($
"运算结果:{result}");
 
一个伸缩性好的程序不应该使线程阻塞,当调用Wait,查询任务的Result属性时,极有可能造成线程池创建新线程,增大的资源的消耗。ContinueWith方法可以在任务完成时执行另一个任务,避免线程的阻塞。
 
	private void StartThread(object sender
, RoutedEventArgs e
)
	{
	    Task
<int> t 
= new Task<int>(n 
=> Sum((int)n
), 1000);
	    t
.Start();
	    Console
.WriteLine("before result out");
	    Task cwt 
= t
.ContinueWith(task 
=> Console
.WriteLine("result is : " + t
.Result
));
	    
	    Console
.WriteLine("after result out");
	}
	
	private int Sum(int countTo
)
	{
	    int sum 
= 0;
	    for (int i 
= 0; i 
< countTo
; i
++)
	    {
	        sum 
+= i
;
	    }
	    return sum
;
	}
	
	输出结果:
	before result 
out
	after result 
out
	result 
is : 499500
 
任务可以启动子任务,且只有当各子任务全部执行结束,父任务才结束:
 
	Task
<int[]> parent 
= new Task<int[]>(() =>
	{
	    var results 
= new int[3];
	    new Task(() => results
[0] = Sum(500), TaskCreationOptions
.AttachedToParent
).Start();
	    new Task(() => results
[1] = Sum(1000), TaskCreationOptions
.AttachedToParent
).Start();
	    new Task(() => results
[2] = Sum(1500), TaskCreationOptions
.AttachedToParent
).Start();
	    return results
;
	});
	
	parent
.ContinueWith(parentTask 
=>
	{
	    foreach (var item 
in parent
.Result
)
	    {
	        Console
.WriteLine(item
.ToString());
	    }
	});
	parent
.Start();
	
	输出结果:
	
124750
	499500
	1124250
 
Task相对于ThreadPool.QueueUserWorkItem具有很多附加属性,如任务状态,父任务的引用,TaskScheduler的引用,回调方法的引用等等,但会增加代价,因为需要为所有这些属性分配内存,所以在不需要这些附加功能时,采用ThreadPool.QueueUserWorkItem能获得更好的资源利用率。
 
System.Threading的Timer类 该类使一个线程池线线程定时调用一个方法。在内部,线程池为所有的Timer对象只使用了一个线程,即单独有一个线程控件所有Timer对象中回调方法的调用,当一个Timer对象到期时,该线程会在内部调用ThreadPool.QueueUserWorkItem,将回调任务添加到线程池队列中。当回调方法执行时间很长,而Timer间隔时间又很短时,会存在线程池多个线程同时执行一个回调方法。 所以要在一个线程池线程上执行定期性发生的后台任务时,采用Timer定时器。 System.Windows.Forms的Timer类及System.Windows.Threading的Dispatcher类的功能相同,与上面定时器的区别是:回调方法只由一个线程完成,就是设置定时器的线程,即在一个线程中设置了DispatcherTimer,那么其回调方法也只在该线程中执行。Task类中几个常用方法: public static bool WaitAll(Task[] tasks):判断tasks是否全部执行完毕; public static bool WaitAny(Task[] tasks):判断tasks中是否存在执行完毕的task; public static Task WhenAll(Task[] tasks):当所有tasks执行完毕后,创建并返回一个新的Task; public static Task WhenAll(Task[] tasks):当tasks中存在已执行完毕的task,创建并返回一个新的Task;TaskScheduler是一个非常重要的抽象,该组件实际上负责如何执行任务,默认的任务调试器将任务放置到线程池的工作线程中,这是TPL的默认选项;. C#5.0引入了新的语言特性,称为异步函数(asynchronous function),它是TPL之上更高级别的抽象,真正简化了异步编程,主要依靠async和await关键字实现; 
参考:C#多线程编程系列 参考:C#多线程总结(纯干货)