延申阅读:
1) 多线程之Thread
2) 多线程之线程池
3) 多线程之Task
一、线程池的概念
线程Thread创建和维护,需要消耗很多系统资源,频繁的创建销毁,使得系统性能下降。因此微软提出了线程池的概念。
我们可以把线程池想像成一个容器,里面装了很多线程,需要的时候从里面取出使用,使用完了扔在里面备用。
线程池(ThreadPool)是一个静态类,可以直接使用。
ThreadPool.GetMinThreads(工作线程数,I/O线程数) 读默认最小线程数
ThreadPool.GetMaxThreads(工作线程数,I/O线程数) 读默认最大线程数
ThreadPool.SetMinThreads(工作线程数,I/O线程数) 设置最小线程数
ThreadPool.SetMaxThreads(工作线程数,I/O线程数) 设置最大线程数
一般默认的最小线程数就是CPU的内核数,比如我的CPU是12核,那么最小线程数是12。
设置最小/最大线程数都不要小于CPU核心数,不然会造成性能问题。
设置最大线程数尽量数量大一些,以防止线程不够用时,进入等待或者死锁状态。
线程池中的线程默认都是后台线程!
线程池适用于运行短而快的任务!
二、线程池的使用
把需要执行的任务放到工作队列中,系统会在适当的时候调用它。
ThreadPool.QueueUserWorkItem
下面例子介绍了线程池的简单使用。
private static AutoResetEvent autoReset = new AutoResetEvent(false);
static void Main(string[] args)
{
//执行委托任务
ThreadPool.QueueUserWorkItem(p =>
{
Console.WriteLine("李白(1)字:" p);
},"太白");
//执行方法任务
ThreadPool.QueueUserWorkItem(ThreadPoolMethod, "太白");
autoReset.WaitOne();//等待线程完成
Console.WriteLine("执行完成!");
}
static void ThreadPoolMethod(object p) //注意参数类型必须是 object
{
Console.WriteLine("李白(2)字:" p);
autoReset.Set();
}
三、异步编程
通过调用线程池的线程,运行委托,可以实现异步方法。
在这之前,我也一直有个困惑,那就是线程不就是并发的吗?不就是异步的吗?那为什么还要使用委托实现异步效果呢?
答案就是线程只是一个运行的过程,本身并不会有返回值,当我们执行一个线程,想要得到这个执行结果时,就要用到异步委托了。
//定义一个要执行的方法
static string ThreadPoolAsyncMethod()
{
return "线程池示例";
}
1、同步委托
static void Main(string[] args)
{
Func<string> tpad = ThreadPoolAsyncMethod; //委托方法
var result = tpad.BeginInvoke(null, null);
result.AsyncWaitHandle.WaitOne(); //等待异步完成
var retValue = tpad.EndInvoke(result); //读取值
Console.WriteLine("操作前");
Console.WriteLine("异步值:" retValue);
Console.WriteLine("操作后");
}
可以看到这里并没有实现异步,主线程一直阻塞,直到tpad委托执行完成。这当然不是我们想要的效果。
2、异步委托
static void Callback(IAsyncResult r)
{
var target=(Func<string>)r.AsyncState;
var result = target.EndInvoke(r);
Console.WriteLine("异步结果:" result);
}
static void Main(string[] args)
{
Func<string> tpad = ThreadPoolAsyncMethod; //委托方法
var result = tpad.BeginInvoke(Callback, tpad); //实现异步的主要操作,就是回调
//result.AsyncWaitHandle.WaitOne(); //等待异步完成
//var retValue = tpad.EndInvoke(result); //读取值
Console.WriteLine("操作前");
//Console.WriteLine("异步值:" retValue);
Console.WriteLine("操作后");
}
可以看到,异步并没有阻塞主线程,同时异步委托并行执行。
注:调用BeginInvoke会自动调用一个线程池中的线程,如果线程池中没有线程,系统自动创建。